Jak radzić sobie ze sprawdzonymi wyjątkami, których nigdy nie można wyrzucić?

35

Przykład:

foobar = new InputStreamReader(p.getInputStream(), "ISO-8859-1");

Ponieważ kodowanie jest zakodowane na stałe i poprawne, konstruktor nigdy nie zgłosi wyjątku UnsupportedEncodingException zadeklarowanego w specyfikacji (chyba że implementacja Java jest zepsuta, w którym to przypadku i tak się zgubiłem). W każdym razie Java zmusza mnie do zajęcia się tym wyjątkiem.

Obecnie tak to wygląda

try {
    foobar = new InputStreamReader(p.getInputStream(), "ISO-8859-1");
}
catch(UnsupportedEncodingException e) { /* won't ever happen */ }

Wszelkie pomysły, jak to zrobić lepiej?

użytkownik 281377
źródło
4
W przypadku prawdziwego wyzwania napisz test jednostkowy, aby upewnić się, że ten haczyk rzeczywiście działa.
Jay Elston
1
`rzuć nowy ImpossibleException („ Uruchom ponownie wszechświat. Rzeczy są popsute ”, e);
Chris Cudmore,
1
„Nigdy się nie wydarzy” będzie ...
Thorbjørn Ravn Andersen: może po zmianie programu, ale przynajmniej w obecnym stanie, żaden zestaw danych wejściowych nie może wywołać wyjątku.
user281377,
W powyższym konkretnym przykładzie zgłoszony jest wyjątek, ponieważ metoda nie wie, czy zostanie wywołana z obsługiwanym, czy nie obsługiwanym kodowaniem. Można by to obejść, gdyby istniał inny konstruktor, który zakłada, że ​​podano standardowy zestaw znaków, który nie musiałby deklarować, że zostanie zgłoszony wyjątek.
Jordania

Odpowiedzi:

27

Moim nawykiem jest, aby być po bezpiecznej stronie, wkładać assertblokadę. Ktoś może trypóźniej zmienić zawartość bloku, a ty chcesz wiedzieć, czy kod się nie powiedzie, prawda?

Péter Török
źródło
Dobry pomysł; prosty assert false;nie dodaje bałaganu i wyjaśnia, że ​​zakładam, że blok catch nigdy nie zostanie wprowadzony.
user281377,
5
@ammoQ, czy można jeszcze dodać wiadomość do Nakręć intencją absolutnie jasne: assert false : "should never happen".
Péter Török
13
Jeszcze lepiej: „Nigdy nie powinno się tak stać, ponieważ Java wymaga obsługi zestawu znaków ISO-8859-1”.
dan04
3
assertoznacza, że ​​asercje są włączone. Rzucam UnexpectedException(co ma tę zaletę, że pozwala mi mieć ślad stosu ...).
Chop
35

Gdybym dostał cent za każdym razem, gdy widziałem log / błąd „To nigdy nie powinno się zdarzyć”, miałbym ... cóż, dwa centy. Ale nadal ...

Puste bloki catch powodują, że moje pająki odczuwają mrowienie, a większość dobrych narzędzi do analizowania kodu narzeka. Za wszelką cenę unikałbym pozostawienia ich pustych. Jasne, teraz wiesz, że błąd nigdy nie może się zdarzyć, ale za rok ktoś przeprowadzi globalne wyszukiwanie w celu zastąpienia „ISO-8859-1” i nagle może być bardzo trudno znaleźć błąd.

assert falsePropozycja jest dobra, ale ponieważ twierdzenia mogą być wyłączone w czasie wykonywania, są bez gwarancji. Użyłbym RuntimeExceptionzamiast tego. Nie trzeba ich wychwytywać podczas wywoływania klas, a jeśli kiedykolwiek wystąpią, będziesz mieć ślad stosu, aby podać pełne informacje.

Fredrik
źródło
28

Zawsze tak robiłem:

try {
    foobar = new InputStreamReader(p.getInputStream(), "ISO-8859-1");
} catch(UnsupportedEncodingException e) {
    throw new AssertionError(e);
}

Może to być trochę gadatliwe (Java to ...), ale przynajmniej dostaniesz błąd asercji, gdy stanie się niemożliwe.

Jeśli implementacja Java jest zepsuta, będziesz chciał otrzymać tak dobry komunikat o błędzie, jak to możliwe, tak szybko, jak to możliwe, zamiast po prostu ignorować niemożliwe. A nawet jeśli realizacja Java nie jest uszkodzony, ktoś mógłby zmienić swój kod do "UTF8"(oops - powinno być "UTF-8"?).

W pierwszej kolejności powinien to być wyjątek czasu wykonywania. JDK jest pełen tego rodzaju złych wyborów.

Joonas Pulakka
źródło
4
Prawdziwą złą decyzją (lub pominięciem, jeśli chcesz) jest to, że nie ma predefiniowanych instancji zestawu znaków dla tych 6 zestawów znaków wymaganych przez Javę. foobar = new InputStreamReader(p.getInputStream(), Charset.ISO_8859_1);- czy nie byłoby to ładniejsze i uniknęło kiedyś błędu na zawsze?
user281377,
3
Biblioteka Guava ma mnóstwo takich rzeczy. Ułatwia życie.
3
Java 1.7+ ma zdefiniowane stałe zestawu znaków: docs.oracle.com/javase/7/docs/api/java/nio/charset/… . Używaj ich w ten sposób: StandardCharsets.UTF_8.displayName ()
Michael
4

Jeśli jesteś jedynym programistą, który kiedykolwiek zobaczy ten kod, powiedziałbym, że jest w porządku, ale jeśli nie, to potraktowałbym go jako realną możliwość lub przynajmniej zmienię komentarz „nigdy się nie wydarzy” na coś bardziej przydatnego.

Thanos Papathanasiou
źródło
4

Część tych wyjątków, która najbardziej mnie denerwuje, to to, że szkodzi mojemu pokryciu kodu.

Kiedy kompulsywnie podchodzę do zasięgu, zwrócę próbę / złapanie, że „nigdy nie może się zdarzyć” (... lub tylko jeśli używam zmutowanej maszyny JVM, która w jakiś sposób zapomniała dołączyć „US-ASCII”) do klasa i metoda, która hermetyzuje to try / catch, i zastępuje zaznaczony wyjątek na jeden ze sposobów wymienionych tutaj (zwykle zgłaszając niesprawdzony wyjątek komunikatem snide).

Następnie moje pokrycie kodu ma trafienie w klasie użyteczności, ale nie we wszystkich odniesieniach do tej operacji rozrzuconych wokół mojego kodu.

Czasami poświęcę trochę czasu na zwinięcie jak operacje w klasie, która ma spójną semantykę. Ale ponieważ jest to dość oczywiste dla moich kolegów z zespołu, co się dzieje, zwykle po prostu staram się, aby było to tak proste, jak tylko mogę i nie martwię się tak bardzo o najlepszy możliwy projekt.

Jednak, jak wspomniano w komentarzu, Guava i inne biblioteki mają sposoby na złagodzenie tego bólu - ale to w zasadzie ta sama strategia. Przenieś irytację poza scenę, aby Twój główny kod nie obejmował zasięgu.

Obrabować
źródło