Soru java: kontrol edilen istisnaları sarma standart yöntemi


Kontrol edilen bir istisnayı ve Guava'nın yaptığı yolu sarmalamanın doğru yolu hakkında oldukça detaylı bir sorum var. (Uzunluk için özür dilerim ama düşünce sürecimi aşağı çekmek istiyorum)


Standart Runnable arayüzü şuna benzer:

public interface Runnable
{
   public void run();
}

nerede run() kontrol edilmiş bir istisna atamıyorum.

Eğer sahip olmak istersem Runnable kontrol edilen istisnaları atayan görevleri sarmak için kullanılır ve bu çağrılara sahip olmak niyetindeyim Runnable.run() bu istisnaları değil, Runnable.run() kendisi, istisnasız bir istisna dışında istisna sarmak zorunda.

Yani bir süredir kullanıyordum:

Runnable r = new Runnable {
   @Override public void run()
   {
       try {
          doNastyStuff();
       }
       catch (NastyException e)
       {
          throw new RuntimeException(e);
       }
   }      
};

ve sonra RuntimeException'ı bir üst düzeyde ele alabilirim. Bunun dışında, gerçekten istediğim şeyin tamamlanmış bir istisnayı ayrı ayrı ele alması gerektiğine inandım, çünkü ben onun semantiklerinin kontrol edilmiş bir istisna sardığını biliyorum, bu yüzden bu yardımcı sınıfı yazdım:

/**
 * Wrapped exception: the purpose of this is just to wrap another exception,
 * and indicate that it is a wrapped exception
 */
public class WrappedException extends RuntimeException
{
    /**
     * @param t any throwable
     */
    public WrappedException(Throwable t)
    {
        super(t);
    }
}

ve sonra bunu yapabilirim:

/* place that produces the exception */
...
catch (NastyException e)
{
   throw new WrappedException(e);
}

...
/* upper level code that calls Runnable.run() */
try
{
   ...
   SomeOtherNastyCode();
   r.run();
   ...
}
catch (SomeOtherNastyException e)
{
   logError(e);
}
catch (WrappedException e)
{
   logError(e.getCause());
}

ve harika çalışıyor gibi görünüyor.

Ama şimdi düşünüyorum, eğer bir kütüphanede ve kütüphaneyi kullanan bir uygulamada kullanmak istersem, şimdi ikisi de WrappedException'a bağlı, bu yüzden her yerde içerebileceğim bir temel kütüphanede olmalı.

Bu da bana Guava'nın standart bir WrappedException sınıfına sahip olduğunu düşünüyor. Yani sadece yapabilirim

throw new WrappedException(e);

veya

throw Exceptions.wrap(e);

veya

Exceptions.rethrow(e);

Sadece Guava'ya baktım ve bulundu Throwables hangisi Throwables.propagate() benzer görünüyor, ancak yalnızca kontrol istisnalarını sarar. RuntimeExceptionRuntimeException özel bir alt sınıfı yerine.

Hangi yaklaşım daha iyi? RuntimeException ile karşılaştırıldığında özel bir WrappedException kullanmamalı mıyım? Üst düzey kodum, bilgi değeri ekleyen en üstteki istisnayı öğrenmek istiyor.

NullPointerException'ı saran bir NastyException içeren bir RuntimeException varsa, RuntimeException sarf malzemesi bilgi değeri eklemez ve umurumda değil, dolayısıyla günlüğe yazdığım hata NastyException olur.

Bir NastyException adlı bir IllegalArgumentException varsa, IllegalArgumentException genellikle bilgi değerini ekler.

Yani benim hata kodumu yapan üst kodumda şöyle bir şey yapmalıyım:

catch (RuntimeException re)
{
   logError(getTheOutermostUsefulException(re));
}

/** 
 * heuristics to tease out whether an exception
 * is wrapped just for the heck of it, or whether
 * it has informational value
 */
Throwable getTheOutermostUsefulException(RuntimeException re)
{        
   // subclasses of RuntimeException should be used as is
   if (re.getClass() != RuntimeException)
      return re;
   // if a runtime exception has a message, it's probably useful
   else if (re.getMessage() != null)
      return re;
   // if a runtime exception has no cause, it's certainly
   // going to be more useful than null
   else if (re.getCause() == null)
      return re;
   else
      return re.getCause();
}

Felsefe benim için doğru geliyor ama uygulama kötü hissettiriyor. Sarılmış özel durumları ele almak için daha iyi bir yolu var mı?


ilgili sorular:


17
2018-06-08 21:24


Menşei


En üst düzey işlem kodunuz, günlük dışında ne yapar? Zincirleme özel durumunun yığın izini günlüğe kaydettiğinizde, tüm izini yine de alırsınız, bu yüzden sarılmış olsun veya olmasın farketmez. - artbristol
Sadece giriş yapıyorsanız, yine de her şeyi günlüğe kaydedeceksiniz ve eğer gerçek bir işlem yapıyorsanız, yalnızca yakalayabileceğiniz istisnaları yakalamaya veya yakalamaya çalışın. getCause unwrapping için gereken her şey. - trutheality
@artbristol, @trutheality: Evet, sadece RuntimeException'da logError () öğesini çağırmayı düşündüm. Ama başka bir parçası logError() Benim uygulamamda kullanıcı için bir ekran ve gerçekten herhangi bir gürültü olmadan en kullanışlı mesajı görüntülemem gerekiyor; Neyin yanlış gittiğini anlamak için bir istisna yığını içinde ilerlemeyecekler, bunun yerine mesaja bakacaklar ve eğer sorunu kendi başlarına çözebileceklerse, harika, aksi takdirde bir şikayette bulunuyorum. Bir RuntimeException aldım, ne yapmalıyım? " - Jason S
Sonra bu kodu bir parçası koymak mantıklı olabilir. logError() mesajı kullanıcıya gönderir. - trutheality


Cevaplar:


Bahar nispeten benzer bir şeyle bildiğim tek kütüphanedir. İç içe istisnalar var: NestedRuntimeException ve NestedCheckedException. Bu istisnalar gibi yararlı yöntemler var getMostSpecificCause() veya contains(Class exType). Onların getMessage() yöntem, neden mesajını döndürür (sarma özel durumu zaten bir iletiye sahipse eklenir).

Spring'in veri erişim istisnası hiyerarşisinde kullanılır. Fikir, her bir veritabanı satıcısının kendi JDBC sürücülerinde farklı istisnalar ortaya çıkarmasıdır. Bahar bunları yakalar ve onları daha jenerik hale getirir. DataAccessExceptions. Bunun bir diğer yararı, kontrol edilen istisnaların otomatik olarak çalışma zamanı istisnalarına dönüştürülmesidir.

Öyle söyleniyor ki, bu çok karmaşık bir kod değil ve eminim kod tabanınızda benzer bir şey yapabilirsiniz. Sadece bunun için bir Bahar bağımlılığı eklemenize gerek yok.

Eğer senin yerinde olsaydım, o zaman ve oradaki şeyleri gerçekten halledemezseniz, istisnaları çok yakında "açmaya" çalışmam. Küresel istisnalar zincirine bakacak, ilk "anlamlı" istisnayı bulabilecek ve akıllı bir hata mesajı çıkaracak olan global bir ExceptionHandler'la balonlaşmak (sarılı olsun ya da olmasın). Bunu yapamazsanız, hata raporlarının amacı için "teknik hatayı" yazdırır ve hata mesajının ayrıntılarına ya da bir çeşit günlük dosyasına tüm yığın izini eklerim. Ardından, hatayı düzeltin ve / veya bunun yerine daha anlamlı bir istisna atın.

Guava en Throwables.getCausalChain () istisna işlemlerini basitleştirmek için ilgi çekici olabilir:

Iterables.filter(Throwables.getCausalChain(e), IOException.class));

Düzenle:

Sorununuz hakkında biraz daha düşündüm ve sanırım özel bir "WrapperException" türü ile istisnalarınızı kaydırma konusunda gerçekten endişelenmenize gerek yok. En mantıklı olanı kullanarak onları sarmalısın: ya basit RuntimeException (Guava en Throwables.propagate() Orada yararlı olabilir), bir RuntimeException Ek bir hata mesajı veya uygun olduğunda daha anlamlı bir istisna türü ile.

Java'nın nedensel zincir mekanizması, yine de kök nedenine ulaşmanıza izin verecektir. Kodunuzdaki her yeri sarmalayan istisna konusunda endişelenmenize gerek yok. Yönetmek için genel bir istisna eylemcisi yazın ve her yerde standart istisna kabarcıklarını kullanın.


7
2018-06-08 23:04





Genel olarak, kontrol edilmemiş istisnaları kontrol edilmeyen bir istisna ile sarmaktan kaçınmak en iyisidir (bkz. İşte ve İşte). Bunu aşmanın bazı yolları:

  1. kullanım istenebilen Runnable yerine
  2. Kullan Executors Bohemian gibi çerçeve önerdi
  3. Alt sınıf Runnable (veya kullandığınız arabirim / sınıf) ve belki de bir yöntemden sonra çalıştırma sırasında istisnaları kontrol etmek için bir yol ekleyin public Exception getRunException() veya benzeri.

Kontrol edilmiş bir istisna sarmanız gerekiyorsa, bunu yapmanın en iyi yolunun zaten yaptığınız gibi olduğunu düşünüyorum. RuntimeException. Ama eğer mümkün olursa, bundan kaçınmaya çalışacağım.


0
2018-06-08 23:18



Runnable'ın kontrol edilen istisnaları sarma ihtiyacını göstermek için sadece bir örnek olduğunu düşünüyorum. - Steven Benitez
@Steven: evet, anladın mı? - Jason S
Anlıyorum, cevabımı değiştirdim - Kevin K