Jak mogę przekazać zbiór wyjątków jako główną przyczynę?

52

Jakaś metoda myMethodwywołuje kilka równoległych wykonywania i oczekuje na ich zakończenie.

Te równoległe wykonywanie może zakończyć się wyjątkami. Więc myMethoddostaje listę wyjątków.

Chcę przekazać listę wyjątków jako główną przyczynę, ale główną przyczyną może być tylko jeden wyjątek. Jasne, że mogę stworzyć własny wyjątek, aby osiągnąć to, czego chcę, ale chcę wiedzieć, czy Java, Spring lub Spring Batch ma coś takiego od razu po wyjęciu z pudełka.

gstackoverflow
źródło
3
.NET ma, AggregateExceptionktóry zawiera listę wyjątków. Ten pomysł powinien dotyczyć także Javy.
usr
1
Zobacz także stackoverflow.com/q/8946661/821436
Przywróć Monikę - M. Schröder

Odpowiedzi:

49

Nie jestem pewien, czy to zrobię (chociaż biorąc pod uwagę JavaDoc, nie mogę powiedzieć, dlaczego się waham), ale istnieje lista wyłączonych wyjątków Throwable, do których można dodać za pośrednictwem addSuppressed. Wydaje się, że JavaDoc nie mówi, że jest to przeznaczone wyłącznie dla JVM w try-with-resources:

Dołącza określony wyjątek do wyjątków, które zostały pominięte w celu dostarczenia tego wyjątku. Ta metoda jest bezpieczna dla wątków i zwykle wywoływana (automatycznie i niejawnie) przez instrukcję try-with-resources.

Wyłączenie jest włączone, chyba że zostanie wyłączone za pomocą konstruktora. Gdy wyłączenie jest wyłączone, ta metoda nie robi nic poza sprawdzeniem poprawności swojego argumentu.

Zauważ, że gdy jeden wyjątek powoduje inny wyjątek, pierwszy wyjątek jest zwykle wychwytywany, a następnie drugi wyjątek jest zgłaszany w odpowiedzi. Innymi słowy, istnieje związek przyczynowy między tymi dwoma wyjątkami. Przeciwnie, istnieją sytuacje, w których dwa niezależne wyjątki mogą być zgłaszane w blokach kodu rodzeństwa, w szczególności w bloku try instrukcji try-with-resources i generowanym przez kompilator bloku ostatecznie, który zamyka zasób. W takich sytuacjach można propagować tylko jeden z zgłoszonych wyjątków. W instrukcji try-with-resources, gdy istnieją dwa takie wyjątki, propagowany jest wyjątek pochodzący z bloku try, a wyjątek z bloku ostatecznie jest dodawany do listy wyjątków pomijanych przez wyjątek z bloku try. Jako wyjątek rozwija stos,

Wyjątek mógł zostać pominięty, a jednocześnie spowodowany przez inny wyjątek. To, czy wyjątek ma przyczynę, jest semantycznie znane w momencie jego utworzenia, w przeciwieństwie do tego, czy wyjątek będzie tłumił inne wyjątki, które zwykle określa się dopiero po zgłoszeniu wyjątku.

Zauważ, że kod napisany przez programistę może również skorzystać z wywołania tej metody w sytuacjach, w których istnieje wiele wyjątków od rodzeństwa i można propagować tylko jeden.

Zwróć uwagę na ostatni akapit, który wydaje się pasować do twojego przypadku.

TJ Crowder
źródło
[...] czy wyjątek zniesie inne wyjątki [...] zazwyczaj określa się dopiero po zgłoszeniu wyjątku. Wyobrażam sobie, że tak się nie stanie, gdy wiele równoległych wyłączeń zostanie zebranych.
GOTO 0
24

Wyjątki i ich przyczyny są zawsze tylko 1: 1: możesz zgłosić jeden wyjątek, a każdy wyjątek może mieć tylko jeden przyczynę (która znowu może mieć jedną przyczynę ...).

Można to uznać za wadę projektową, zwłaszcza gdy rozważa się zachowanie wielowątkowe zgodnie z opisem.

To jeden z powodów, dla których dodano Javę 7 addSuppressed do wrzutu, który może w zasadzie dołączyć dowolną liczbę wyjątków do jednego innego (drugą główną motywacją było wypróbowanie zasobów, które wymagały sposobu obsługi wyjątków w bloku ostatecznie bez dyskretnego upuszczania) im).

Zasadniczo, gdy masz 1 wyjątek powodujący niepowodzenie procesu, dodajesz go jako przyczynę wyjątku wyższego poziomu, a jeśli masz więcej, dodajesz je do oryginalnego za pomocą addSuppressed. Chodzi o to, że ten pierwszy wyjątek „tłumił” pozostałe, stając się członkiem „prawdziwego łańcucha wyjątków”.

Przykładowy kod:

Exception exception = null;
for (Foobar foobar : foobars) {
  try {
    foobar.frobnicate();
  } catch (Exception ex) {
    if (exception == null) {
      exception = ex;
    } else {
      exception.addSuppressed(ex);
    }
  }
}
if (exception != null) {
  throw new SomethingWentWrongException(exception);
}
Joachim Sauer
źródło
4
Nie zrobiłbym tego tak, jak sugerujesz, chyba że jeden z podstawowych wyjątków można naprawdę wyróżnić jako „główny”. Jeśli wybierzesz arbitralnie jeden z wyjątków jako główny, a pozostałe jako pomijane, zapraszasz osobę dzwoniącą do zignorowania wyłączonych wyjątków i po prostu zgłoś główny - nawet jeśli „głównym” wyjątkiem jest TypoInUserInputException i jeden z te pomijane to DatabaseCorruptException.
Ilmari Karonen,
1
… Zamiast tego oznaczę wszystkie podstawowe wyjątki jako pomijane przez wyjątek SomethingWentWrongException i przekażę temu wyjątkowi komunikat wyraźnie wskazujący, że powinien nastąpić jeden lub więcej wyłączonych wyjątków , np. Coś w rodzaju „ X zadań z Y nie powiodło się, patrz lista błędów poniżej „.
Ilmari Karonen