No tak średnio, bo czasem trzeba łapać wyjątek w lambdzie i trochę słabo to wygląda.
W JEE wygodnie jest oznaczać własne wyjątki adnotacją @ApplicationException, wtedy podczas wystąpienia nie są one ubierane dodatkowo w EJBException
Pod względem wyjątków JavaEE to mistrzostwo świata (biedy).
Wystarczy zapomnieć o tym ApplicationException, i mimo, że Ci się wydaje, że obsługujesz wyjątek w catch to i tak rollback leci (do tego na koniec transakcji poza twoim kodem, żeby było łatwiej).
Do tego nie zawsze, ważne, żeby interceptor go przyczaił (czyli jeśli poleci z wywoałania prywatnej metody to problemu nie ma). Zawsze mnie rozbija jak widze jak ktoś z tym walczy. Tej koncepcji nie da się wytłumaczyć człowiekowi przyzwyczajonemu, że catch robi po prostu catch i wyjątek obsługuje (co za dziwny pomysł w ogóle :-) ).
Tak ogólnie to o wyjątkach - zwłaszcza checked najlepiej juz zapomnieć (tzn. nie tworzyć nowych - co najwyżej żyć z tymi, które są w starym kodzie).
Koncepcja Try, Either lub Optional jest zdecydowanie wygodniejsza/ bezpieczniejsza - mimo, że na pierwszy rzut oka może się to wydawać to samo.
Tu i tu nas kompilator zmusza do "obsługi" problemu jeśli coś z innej metody wyciągamy.
Checked Exception wymusza catch
, a optional .map
lub .orElse
. (Bo .get nie używamy!! jak pisał @Koziołek https://koziolekweb.pl/2017/08/07/kiedy-wypakowac-optional/).
To gdzie jest różnica?
Ano w tzw. referential transparency - Optional (czy inne monady) to po prostu wartość i można z tym pracować jak z każdą zmienną, a Exception to taki dziwny wybuch idący gdzieś poza kodem - zupełnie innym torem.
Gdzie to da się docenić ?
Pierwsze miejsce to Testy - zamiast dziwacznego sprawdzania wyniku z @Test(expected = NullPointerException ) czy innych cudów - robimy
ładne equals :
assertEquals ( Option.none(), repo.getUser("1234");
W testach "parametrycznych", w szczególnośc w Junit 5 to jest super.
Drugie miejsce to fakt, że teraz możemy nasze "wyjątki" nie tylko propagować "w górę" (czyli kto nas wywołał to dostanie), ale również
przekazywać innym !
Załóżmy, że mamy metodę repo.gimmeAllBros() która zwraca listę użytkowników systemu. I robimy jakiegoś batcha, który co noc bierze wszystkich userów i im wysyła spam :-).
Załóżmy, że to dziwny stary system i aby zebrać dane takiego gościa, to trzeba odpytać kilka baz - nie zawsze się udaje. Zwracamy Either dla każdego.
(załadował się ok, albo byl bład wtedy tylko dajemy Identyfikator).
Ta metoda może mieć postać:
List<Either<Integer, Person>> gimmeAllBros();
I mozemy sobie napisać :
Kopiuj
List<Either<Int, Person>> all = repo.gimmeAllBros();
spamThem(all);
recalculateTheirMoney(all);
I teraz metoda spamThem
może olać to, że niektórzy nie są znalezieni. No bo to tylko spam.
Ale metoda recalculateTheirMoney
, która np. nalicza nowy stan konta musi zareagować i wysłać adminowi, że takich użytkowników konta były nie do ogarnięcia (i tu np. identyfikatory).
Czyli w pewnym sensie możemy propagować wyjątek w drugą stronę(w dół wywołań) i teraz to te wywołane metody są odpowiedzialne, za to żeby ewentualnie zareagować.