Przeczytałem jakiś kod kolegi i stwierdziłem, że często łapie on różne wyjątki, a następnie zawsze zgłasza „RuntimeException”. Zawsze myślałem, że to bardzo zła praktyka. Czy się mylę?
java
exceptions
exception-handling
RoflcoptrException
źródło
źródło
Function
,Predicate
itp) nie są parametryzowane rzuca klauzul. Oznacza to, że musisz łapać, owijać i ponownie rzucać sprawdzonymi wyjątkami w wewnętrznej pętli dowolnej metody stream (). To samo w sobie podpowiada mi, czy sprawdzone wyjątki są złym pomysłem.Odpowiedzi:
Nie znam wystarczającego kontekstu, aby wiedzieć, czy twój kolega robi coś niepoprawnie, czy nie, więc zamierzam się o to spierać w sensie ogólnym.
Nie sądzę, aby zmienianie sprawdzonych wyjątków w jakiś smak wyjątku środowiska wykonawczego było zawsze niewłaściwą praktyką . Sprawdzone wyjątki są często nadużywane i nadużywane przez programistów.
Sprawdzone wyjątki są bardzo łatwe w użyciu, gdy nie są przeznaczone do użycia (warunki niemożliwe do odzyskania, a nawet przepływ kontrolny). Zwłaszcza jeśli sprawdzony wyjątek jest używany dla warunków, z których osoba dzwoniąca nie może się odzyskać, myślę, że uzasadnione jest przekształcenie tego wyjątku w wyjątek czasu wykonywania z pomocnym komunikatem / stanem. Niestety, w wielu przypadkach, gdy ktoś staje w obliczu niemożliwego do odzyskania stanu, ma zazwyczaj pustą blokadę, co jest jedną z najgorszych rzeczy, jakie możesz zrobić. Debugowanie takiego problemu jest jednym z największych problemów, jakie może napotkać programista.
Jeśli więc uważasz, że masz do czynienia z sytuacją możliwą do odzyskania, należy ją odpowiednio obsłużyć, a wyjątku nie należy przekształcać w wyjątek czasu wykonywania. Jeśli sprawdzony wyjątek zostanie zastosowany w przypadku niemożliwych do odzyskania warunków, uzasadnione jest przekształcenie go w wyjątek czasu wykonywania .
źródło
To może być dobre . Proszę przeczytaj:
http://onjava.com/pub/a/onjava/2003/11/19/exceptions.html
Rzucanie sprawdzonych wyjątków i niemożność powrotu do zdrowia nie pomaga.
Niektórzy uważają nawet, że sprawdzone wyjątki nie powinny być w ogóle stosowane. Zobacz http://www.ibm.com/developerworks/java/library/j-jtp05254/index.html
Również z tego samego linku:
źródło
Nie, nie mylisz się. Jego praktyka jest bardzo błędna. Powinieneś zgłosić wyjątek wychwytujący problem, który go spowodował. Wyjątek RunTimeException jest szeroki i przekracza zasięg. Powinien to być wyjątek NullPointerException, ArgumentException itp. Cokolwiek dokładnie opisuje to, co poszło nie tak. Zapewnia to możliwość rozróżnienia problemów, które należy rozwiązać i umożliwienia programowi przetrwania, a błędów, które powinny być scenariuszem „Nie przechodź”. To, co robi, jest tylko nieznacznie lepsze niż „Po wznowieniu błędu dalej”, chyba że w informacjach podanych w pytaniu brakuje czegoś.
źródło
To zależy.
Ta praktyka może być nawet mądra . Istnieje wiele sytuacji (na przykład przy tworzeniu stron internetowych), w których jeśli zdarzy się jakiś wyjątek, nie będziesz w stanie nic zrobić (ponieważ nie możesz na przykład naprawić niespójnej DB z kodu :-), może to zrobić tylko programista). W takich sytuacjach mądrze jest zawinąć zgłoszony wyjątek w wyjątek środowiska wykonawczego i ponownie go wyrzucić. Następnie możesz wychwycić wszystkie te wyjątki w jakiejś warstwie obsługi wyjątków, zalogować błąd i wyświetlić użytkownikowi miły, zlokalizowany kod błędu + komunikat.
Z drugiej strony , jeśli wyjątkiem nie jest środowisko wykonawcze (jest zaznaczone), twórca interfejsu API wskazuje, że wyjątek ten można rozwiązać i należy go naprawić. Jeśli to możliwe, zdecydowanie powinieneś to zrobić.
Innym rozwiązaniem może być ponowne wrzucenie tego sprawdzonego wyjątku do warstwy wywołującej, ale jeśli nie możesz go rozwiązać, w którym wystąpił wyjątek, prawdopodobnie nie będziesz w stanie rozwiązać go również tutaj ...
źródło
exception handling layer
- np. W aplikacji internetowej, filtrze.Chciałbym otrzymywać komentarze na ten temat, ale zdarza mi się, że zdarza się, że niekoniecznie jest to zła praktyka. (Lub okropnie źle). Ale może się mylę.
Często używany interfejs API zgłasza wyjątek, którego nie można sobie wyobrazić w konkretnym przypadku użycia. W takim przypadku wydaje się być w porządku, aby zgłosić wyjątek RuntimeException z wychwyconym wyjątkiem jako przyczyną. Zgłoszenie tego wyjątku prawdopodobnie spowoduje błąd programowy i nie znajdzie się w granicach poprawnej specyfikacji.
Zakładając, że wyjątek RuntimeException nie zostanie później złapany i zignorowany, nie ma go w pobliżu OnErrorResumeNext.
OnErrorResumeNext miałby miejsce, gdy ktoś złapie wyjątek i po prostu go zignoruje lub po prostu wydrukuje. Jest to strasznie zła praktyka w prawie wszystkich przypadkach.
źródło
TL; DR
Przesłanka
Wniosek
Możemy ponownie sprawdzić sprawdzony wyjątek jako wyjątek środowiska wykonawczego, jeśli kod propagacyjny lub kod interfejsu zakłada, że podstawowa implementacja zależy od stanu zewnętrznego, gdy jest to oczywiste.
W tej sekcji omówiono, kiedy należy zgłosić jeden z wyjątków. Możesz przejść do następnego poziomego paska, jeśli chcesz przeczytać bardziej szczegółowe wyjaśnienie wniosku.
Kiedy należy zgłosić wyjątek czasu wykonywania? Zgłaszasz wyjątek środowiska wykonawczego, gdy jest jasne, że kod jest niepoprawny, a odzyskiwanie jest odpowiednie poprzez modyfikację kodu.
Na przykład należy zgłosić wyjątek czasu wykonywania dla następujących elementów:
Spowoduje to wygenerowanie podziału przez zero wyjątku czasu wykonywania. Jest to właściwe, ponieważ kod jest uszkodzony.
Lub na przykład, oto część
HashMap
konstruktora:Aby naprawić początkową pojemność lub współczynnik obciążenia, należy edytować kod, aby upewnić się, że przekazywane są prawidłowe wartości. Nie zależy to od działania jakiegoś odległego serwera, od aktualnego stanu dysku, plik lub inny program. Wywołanie konstruktora z niepoprawnymi argumentami zależy od poprawności kodu wywołującego, czy to z powodu niewłaściwego obliczenia, które doprowadziło do niepoprawnych parametrów lub niewłaściwego przepływu, który przeoczył błąd.
Kiedy należy zgłosić sprawdzony wyjątek? Zgłaszasz wyjątek, gdy problem można naprawić bez zmiany kodu. Lub, mówiąc inaczej, rzucasz sprawdzony wyjątek, gdy błąd jest związany ze stanem, gdy kod jest poprawny.
Teraz słowo „odzyskać” może być trudne. Może to oznaczać, że znajdziesz inny sposób na osiągnięcie celu: Na przykład, jeśli serwer nie odpowiada, powinieneś wypróbować następny serwer. Jeśli tego rodzaju odzyskiwanie jest możliwe w twoim przypadku, to świetnie, ale to nie jedyna rzecz, którą oznacza odzyskiwanie - odzyskiwanie może po prostu wyświetlać użytkownikowi okno dialogowe błędu wyjaśniające, co się stało, lub jeśli jest to aplikacja serwerowa, może to być wysyłanie wiadomości e-mail do administratora, a nawet zwykłe rejestrowanie błędu w odpowiedni i zwięzły sposób.
Weźmy przykład wspomniany w odpowiedzi mrmugglesa:
To nie jest poprawny sposób obsługi sprawdzonego wyjątku. Sama niezdolność do obsługi wyjątku w zakresie tej metody nie oznacza, że aplikacja powinna ulec awarii. Zamiast tego należy propagować go do wyższego zakresu, takiego jak:
Co pozwala na odzyskanie przez dzwoniącego:
Sprawdzone wyjątki są narzędziem analizy statycznej, wyjaśniają programiście, co może pójść nie tak w określonym wywołaniu bez konieczności uczenia się implementacji lub przechodzenia przez proces prób i błędów. Ułatwia to upewnienie się, że żadna część przepływu błędów nie zostanie zignorowana. Ponowne sprawdzenie sprawdzonego wyjątku jako wyjątku środowiska wykonawczego działa przeciwko tej oszczędzającej pracę funkcji analizy statycznej.
Warto również wspomnieć, że warstwa wywołująca ma lepszy kontekst większego schematu rzeczy, jak pokazano powyżej. Może być wywoływanych wiele przyczyn
dataAccessCode
, konkretny powód połączenia jest widoczny tylko dla dzwoniącego - w ten sposób jest w stanie podjąć lepszą decyzję w sprawie prawidłowego odzyskania po awarii.Teraz, gdy udało nam się wyczyścić to rozróżnienie, możemy przystąpić do dedukcji, gdy będzie w stanie ponownie sprawdzić sprawdzony wyjątek jako wyjątek czasu wykonywania.
Biorąc pod uwagę powyższe, kiedy właściwe jest ponowne zwrócenie sprawdzonego wyjątku jako RuntimeException? Gdy używany kod zakłada zależność od stanu zewnętrznego, ale można wyraźnie stwierdzić, że nie zależy on od stanu zewnętrznego.
Rozważ następujące:
W tym przykładzie kod się propaguje,
IOException
ponieważ interfejs APIReader
jest przeznaczony do uzyskiwania dostępu do stanu zewnętrznego, jednak wiemy, żeStringReader
implementacja nie ma dostępu do stanu zewnętrznego. W tym zakresie, w którym możemy z pewnością stwierdzić, że części biorące udział w połączeniu nie mają dostępu do IO ani żadnego innego stanu zewnętrznego, możemy bezpiecznie przywrócić wyjątek jako wyjątek czasu wykonywania bez zadziwiających kolegów, którzy nie są świadomi naszej implementacji (i być może zakładając, że kod dostępu do IO wyrzuci anIOException
).Powodem ścisłego sprawdzania wyjątków zależnych od stanu zewnętrznego jest to, że nie są one deterministyczne (w przeciwieństwie do wyjątków zależnych od logiki, które będą przewidywalnie odtwarzane za każdym razem dla wersji kodu). Na przykład, jeśli spróbujesz podzielić przez 0, zawsze wygenerujesz wyjątek. Jeśli nie podzielisz przez 0, nigdy nie wygenerujesz wyjątku i nie będziesz musiał obsługiwać tego wyjątku, ponieważ nigdy się nie zdarzy. W przypadku uzyskania dostępu do pliku, sukces raz nie oznacza, że odniesiesz sukces następnym razem - użytkownik mógł zmienić uprawnienia, inny proces mógł go usunąć lub zmodyfikować. Więc zawsze musisz poradzić sobie z tym wyjątkowym przypadkiem, w przeciwnym razie możesz mieć błąd.
źródło
Do samodzielnych aplikacji. Jeśli wiesz, że Twoja aplikacja nie może obsłużyć wyjątku, możesz zamiast rzucać zaznaczony RuntimeException, rzucić błąd, pozwolić aplikacji na awarię, mieć nadzieję na raporty o błędach i naprawić aplikację. (Zobacz odpowiedź mrmuggles, aby uzyskać bardziej szczegółową dyskusję na temat zalet i wad sprawdzonych kontra niezaznaczonych).
źródło
Jest to powszechna praktyka w wielu ramach. Np.
Hibernate
Robi dokładnie to. Chodzi o to, że interfejsy API nie powinny być uciążliwe dla klienta iException
są uciążliwe, ponieważ musisz jawnie napisać kod, aby obsłużyć je w miejscu, w którym wywołujesz interfejs API. Ale to miejsce może nie być właściwym miejscem do obsługi wyjątku.Właściwie to szczerze mówiąc jest to „gorący” temat i wiele sporów, więc nie będę się sprzeciwiał, ale powiem, że to, co robi / proponuje twój przyjaciel, nie jest niczym niezwykłym ani niezwykłym.
źródło
Cały ten „sprawdzony wyjątek” to zły pomysł.
Programowanie strukturalne pozwala na przekazywanie informacji między funkcjami (lub, w języku Java, metodami) tylko wtedy, gdy są one „w pobliżu”. Dokładniej, informacje można przenosić między funkcjami tylko na dwa sposoby:
Od dzwoniącego do rozmówcy poprzez przekazywanie argumentów.
Od odbiorcy do osoby dzwoniącej, jako wartości zwracane.
To jest zasadniczo dobra rzecz. Oto, co pozwala ci lokalnie uzasadnić swój kod: jeśli potrzebujesz zrozumieć lub zmodyfikować część programu, wystarczy spojrzeć na tę część i inne „pobliskie”.
Jednak w niektórych okolicznościach konieczne jest przesłanie informacji do „odległej” funkcji, bez wiedzy nikogo w środku. To właśnie wtedy trzeba zastosować wyjątki. Wyjątkiem jest tajna wiadomość wysyłana z modułu wywołującego (dowolna część kodu może zawierać
throw
instrukcję) do modułu obsługi (dowolna część kodu może zawieraćcatch
blok zgodny z wyjątkiem, który byłthrow
n).Sprawdzone wyjątki niszczą tajemnicę mechanizmu, a wraz z nim samą przyczynę jego istnienia. Jeśli funkcja może pozwolić na to, by jej rozmówca „znał” pewną informację, wystarczy wysłać tę informację bezpośrednio jako część wartości zwracanej.
źródło
Może to zależeć od przypadku. W niektórych scenariuszach mądrze jest robić to, co robi twój przyjaciel, na przykład, gdy udostępniasz interfejs API niektórym klientom i chcesz, aby klient był najmniej świadomy szczegółów implementacji, gdzie wiesz, że pewne wyjątki implementacji mogą być specyficzne dla szczegóły implementacji i niewymienialne dla klienta.
Trzymając z dala sprawdzone wyjątki, możesz ujawnić interfejsy API, które umożliwiłyby klientowi napisanie czystszego kodu, ponieważ sam klient może wstępnie sprawdzać poprawność wyjątkowych warunków.
Na przykład Integer.parseInt (String) pobiera ciąg znaków i zwraca jego całkowity odpowiednik i zgłasza NumberFormatException w przypadku, gdy ciąg nie jest liczbą. Teraz wyobraź sobie, że przesłanie formularza z polem
age
jest konwertowane za pomocą tej metody, ale klient już zapewniłby weryfikację z jego strony, więc nie ma sensu wymuszać sprawdzania wyjątku.źródło
Naprawdę jest tutaj kilka pytań
Ogólna zasada jest taka, że należy sprawdzić wyjątki, które osoba dzwoniąca może wychwycić i odzyskać. Inne wyjątki (te, w których jedynym rozsądnym rezultatem jest przerwanie całej operacji lub gdy uważasz, że są one na tyle mało prawdopodobne, że martwienie się o ich obsługę nie jest tego warte) powinny zostać odznaczone.
Czasami twoja ocena, czy wyjątek zasługuje na złapanie i odzyskanie, różni się od oceny interfejsu API, z którym pracujesz. Czasami kontekst ma znaczenie, wyjątek, który jest wart uwagi w jednej sytuacji, może nie być wart uwagi w innej. Czasami twoja ręka jest wymuszona przez istniejące interfejsy. Tak więc istnieją uzasadnione powody, aby zamienić sprawdzony wyjątek w niesprawdzony wyjątek (lub na inny typ sprawdzonego wyjątku)
Po pierwsze i najważniejsze upewnij się, że korzystasz z mechanizmu łączenia wyjątków. W ten sposób informacje z oryginalnego wyjątku nie zostaną utracone i można je wykorzystać do debugowania.
Po drugie, musisz zdecydować, jakiego typu wyjątku użyć. Użycie zwykłego wyjątku czasu wykonywania utrudnia dzwoniącemu ustalenie, co poszło nie tak, ale jeśli dzwoniący próbuje ustalić, co poszło źle, może to oznaczać, że nie należy zmieniać wyjątku, aby usunąć zaznaczenie.
źródło
W pytaniu boolowskim trudno jest odpowiedzieć inaczej po dwóch kontrowersyjnych odpowiedziach, ale chciałbym dać wam perspektywę, o której nawet wspomniałem w kilku miejscach, nie było wystarczająco podkreślone ze względu na jej znaczenie.
Z biegiem lat odkryłem, że zawsze ktoś jest zdezorientowany, jeśli chodzi o trywialny problem, brakuje mu zrozumienia niektórych podstaw.
Nakładanie warstw. Aplikacja (przynajmniej powinna być) stos warstw jedna na drugiej. Jednym z ważnych oczekiwań dotyczących dobrego nakładania warstw jest to, że niższe warstwy zapewniają funkcjonalność dla potencjalnie wielu składników z górnej warstwy.
Załóżmy, że twoja aplikacja ma następujące warstwy od podstaw: NET, TCP, HTTP, REST, MODEL DANYCH, BIZNES.
Jeśli twoja warstwa biznesowa chce wykonać połączenie reszty ... poczekaj sekundę. Dlaczego to powiedziałem? Dlaczego nie powiedziałem żądania HTTP lub transakcji TCP ani nie wysłałem pakietów sieciowych? Ponieważ nie mają one znaczenia dla mojej warstwy biznesowej. Nie zamierzam się nimi zajmować, nie będę zaglądać w ich szczegóły. Jestem całkowicie w porządku, jeśli są głęboko w wyjątkach, które dostaję jako przyczynę i nie chcę wiedzieć, że w ogóle istnieją.
Co więcej, źle jest, jeśli znam szczegóły, ponieważ jeśli jutro chciałbym zmienić podkreślające protokoły transportowe dotyczące szczegółów specyficznych dla protokołu TCP, oznacza to, że moja abstrakcja REST nie wykonała dobrej pracy, aby się z niej wyodrębnić konkretne wdrożenie.
Przy przenoszeniu wyjątku z warstwy na warstwę ważne jest, aby ponownie zapoznać się z każdym jego aspektem i jaki sens ma to dla abstrakcji, jaką zapewnia bieżąca warstwa. Może być zastąpienie wyjątku innym, może łączyć kilka wyjątków. Może również zmienić je z zaznaczonego na niezaznaczone lub odwrotnie.
Oczywiście, faktyczne miejsca, o których wspomniałeś, mają sens, to inna historia, ale ogólnie - tak, to może być dobra rzecz do zrobienia.
źródło
W mojej opinii,
Na poziomie ramowym powinniśmy być wyjątkami środowiska wykonawczego catch, aby zredukować więcej bloków try catch dla wywołującego w tym samym miejscu.
Na poziomie aplikacji rzadko wychwytujemy wyjątki czasu wykonywania i myślę, że ta praktyka była zła.
źródło