TL; DR
Przesłanka
- Wyjątki czasu wykonywania powinny być zgłaszane, gdy błąd jest nieusuwalny: gdy błąd znajduje się w kodzie i nie zależy od stanu zewnętrznego (dlatego odzyskiwanie poprawiałoby kod).
- Zaznaczone wyjątki należy zgłaszać, gdy kod jest poprawny, ale stan zewnętrzny nie jest zgodny z oczekiwaniami: brak połączenia sieciowego, nie znaleziono pliku lub jest on uszkodzony itp.
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:
float nan = 1/0;
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:
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
// more irrelevant code...
}
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:
public void dataAccessCode(){
try{
..some code that throws SQLException
}catch(SQLException ex){
throw new RuntimeException(ex);
}
}
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:
public Data dataAccessCode() throws SQLException {
// some code that communicates with the database
}
Co pozwala na odzyskanie przez dzwoniącego:
public void loadDataAndShowUi() {
try {
Data data = dataAccessCode();
showUiForData(data);
} catch(SQLException e) {
// Recover by showing an error alert dialog
showCantLoadDataErrorDialog();
}
}
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:
StringReader sr = new StringReader("{\"test\":\"test\"}");
try {
doesSomethingWithReader(sr); // calls #read, so propagates IOException
} catch (IOException e) {
throw new IllegalStateException(e);
}
W tym przykładzie kod się propaguje, IOException
ponieważ interfejs API Reader
jest przeznaczony do uzyskiwania dostępu do stanu zewnętrznego, jednak wiemy, że StringReader
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 an IOException
).
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.