2015-05-27 29 views
5

Załóżmy, że obsługuję FooException i BarException. Załóżmy, że oba są niezaznaczonymi wyjątkami.Jak uzyskać poprawnie powiązany ślad stosu dla wyjątków zgłaszanych podczas obsługi innych wyjątków?

Co chcę zobaczyć w stacktrace jest:

com.bar.BarException: Bar Message 
    at com.baz.BazCode(BazCode.java:123) 
    ... 
Caused by: com.foo.FooException: Foo Message 
    at com.baz.BazCode(BazCode.java:321) 
    .... 
Caused by: ... 

Jednak domyślnie cały zapis FooException zostaną usunięte z stacktrace. Na przykład:

// In a class written by me 
/** 
    * ... 
    * @throws FooException if foo happens 
    * @throws BarException if bar happens 
    */ 
public void upperFrame() { 
    try { 
     foo.doSomething(); 
    } catch (FooException foo) { 
     bar.doSomethingElse(); 
    } 
} 

// In class Bar (not written by me) 
public void doSomethingElse() { 
    if (someConditionWhichHappensToBeTrueInThisScenario()) { 
     throw new BarException("Hello Bar World"); // At this point, FooException gets erased from the stack trace 
    } 
} 

Jeśli BarException ma (message, cause) konstruktora wtedy mogę śledzić raczej surowy rodzaj procesu „instrukcja klonowania”, aby osiągnąć mój cel:

try { 
    foo.doSomething(); 
} catch (FooException foo) { 
    try { 
     bar.doSomethingElse(); 
    } catch (BarException bar) { 
     BarException bar2 = new BarException(bar.getMessage(), foo); 
     bar2.setStackTrace(bar.getStackTrace()); 
     throw bar2; 
    } 
} 

Jednak jeśli BarException nie ma takich konstruktor (np ClassCastException) to jestem zredukowana do robienia rzeczy tak:

try { 
    foo.doSomething(); 
} catch (FooException foo) { 
    try { 
     bar.doSomethingElse(); 
    } catch (BarException bar) { 
     RuntimeException e = new RuntimeException("com.bar.BarException: " + bar.getMessage(), foo); 
     e.setStackTrace(bar.getStackTrace()); 
     throw e; 
    } 
} 

jest to niebezpieczne be powoduje, że e ma niewłaściwy typ i dlatego może nie być poprawnie obsługiwany przez wyższe ramki.

Czy istnieje sposób "najlepszej praktyki" radzenia sobie z tą sytuacją?

+0

Czy ślad stack'u 'FooException' stracił podczas tworzenia' BarException' z przyczyną Foo? Czy mógłbyś zrobić coś takiego jak 'bar.getCause(). GetStackTrace()'? –

+0

@KaspervandenBerg Edytowałem moje pytanie, aby pokazać, jak 'FooException' ginie. Nie jest związane z żadnym tworzeniem wyjątku z przyczyną, jest to wyjątek generowany przez metodę 'doSomethingElse'. – Kidburla

Odpowiedz

4

Jednym z rozwiązań jest użycie metody Throwable#initCause(Throwable):

bar.initCause(foo); 
+0

Dobra rzecz (nie zauważyłem tej metody, ponieważ szukałem tylko metody 'setCause') ... o ile' BarException' nie zdefiniował już przyczyny. Jednak nie znalazłem jeszcze żadnej klasy wyjątków (przynajmniej w specyfikacji java), która definiuje przyczynę, ale nie ma konstruktora '(message, cause)'. – Kidburla

1

Dopóki przekazać oryginalny wyjątek nowym jako argument, że tworzy „spowodowane przez” łańcucha i ślad stosu jest zachowana . Twoje przypadki użycia wydają się nieco dziwne. Dla mnie wyjątek podczas odzyskiwania lub obsługi błędu z więcej niż niektórych rejestrowania jest kolejnym błędem, a nie "spowodowany" przez inny błąd. Chciałbym po prostu zalogować "foo" i rzucić "pasek".

W niektórych przypadkach wydaje mi się, że Twój sposób może mieć sens. Aby to zrobić, możesz przekazać "foo" jako doSomethingElse (foo) i rzucić nowy wyjątek BarException (foo), gdy coś pójdzie nie tak. Prawie wszystkie standardowe wyjątki obsługują ten konstruktor, a jeśli potrzebujesz stworzyć własne, po prostu utwórz konstruktora, który je deleguje.

Ja osobiście nie użyłbym ich tak, jak wydaje się. Używam ich do rzucania innego rodzaju wyjątku niż ten, który złapałem, jeśli z jakiegoś powodu chcę innego typu. Na przykład, aby tłumaczyć na wyjątki określonego typu do mojej aplikacji lub tłumaczyć zaznaczone na niezaznaczone w przypadkach, w których ma to sens. W takich przypadkach nadal dobrze jest zachować oryginalny wyjątek i pełne "spowodowane przez .." śledzenie ..

+0

"Po prostu logowałbym foo i rzucałam pasek": Wypisywanie wyjątków, a następnie ich odrzucanie nie jest dobrą praktyką. Co się stanie, jeśli inna biblioteka będzie zależała od Twojego kodu i korzystała z niestandardowego programu rejestrującego (takiego jak rejestrowanie JMS)?"Niemal wszystkie standardowe wyjątki obsługują ten konstruktor" nie, "ClassCastException" to tylko jeden z przykładów – Kidburla

+0

"Osobiście nie użyłbym ich tak, jak wydaje się, ale używam ich do wyrzucania innego rodzaju wyjątku do tego Złapałem "- co sprawia wrażenie, że to nie jest to, co próbuję zrobić? Wyjątki, takie jak "ClassCastException", mogą nadal występować, gdy próbujesz wyrzucić inny typ wyjątku do tego, który złapałem. – Kidburla

+0

"Aby to zrobić, możesz przekazać foo jako coś, co robiSomethingElse (foo)" - jak powiedziałem w moim pytaniu, doSomethingElse nie jest w klasie napisanej przeze mnie. I nie obsługuje przekazywania wyjątku jako argumentu. – Kidburla