Как бороться с проверенными исключениями, которые никогда не могут быть выброшены

35

Пример:

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

Поскольку кодировка жестко запрограммирована и корректна, конструктор никогда не сгенерирует исключение UnsupportedEncodingException, объявленное в спецификации (если только реализация java не нарушена, в этом случае я все равно теряюсь). Во всяком случае, Java заставляет меня все равно иметь дело с этим исключением.

В настоящее время это выглядит так

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

Есть идеи как сделать лучше?

user281377
источник
4
Для реальной задачи напишите модульный тест, чтобы убедиться, что этот улов действительно работает.
Джей Элстон
1
`throw new ImpossibleException (« Перезагрузите вселенную. Вещи испорчены », e);
Крис Кадмор
1
"
Thorbjørn Ravn Andersen: это может произойти после изменения программы, но, по крайней мере, в ее текущем состоянии, никакой набор входных данных не может вызвать исключение.
user281377 30.11.11
В вашем конкретном примере выше выдается исключение, потому что метод не знает, будет ли он вызываться с поддерживаемой или не поддерживаемой кодировкой. Обойти это можно было бы, если бы был другой конструктор, который предполагал, что был задан стандартный набор символов, который не должен был бы объявлять, что будет выброшено исключение.
Иордания

Ответы:

27

Моя привычка состоит в том, чтобы просто быть в безопасности, чтобы положить assertв блок улова. Кто-то может tryпозже изменить содержимое блока, и вы действительно хотите знать, если код не работает, не так ли?

Петер Тёрёк
источник
Отличная идея; Простое assert false;не добавляет слишком много беспорядка и дает понять, что я предполагаю, что блок catch никогда не будет введен.
user281377 29.11.11
5
@ammoQ, или вы даже можете добавить сообщение , чтобы сделать ваше намерение совершенно ясно: assert false : "should never happen".
Петер Тёрёк
13
Еще лучше: «Никогда не должно происходить, потому что Java требует поддержки кодировки ISO-8859-1».
Ден04
3
assertподразумевает, что утверждения включены. Я выбрасываю UnexpectedException(что также дает мне возможность отслеживать трассировку стека ...).
Чоп
35

Если бы мне давали цент за каждый раз, когда я видел журнал / ошибку «Это никогда не должно происходить», у меня было бы ... ну, два цента. Но до сих пор...

Пустые блоки захвата заставляют мои чувства паука покалывать, и большинство хороших инструментов анализа кода жалуются. Я хотел бы любой ценой оставить их пустыми. Конечно, теперь вы знаете, что ошибки никогда не произойдет, но через год кто-то делает глобальный поиск-замену «ISO-8859-1», и вдруг у вас может возникнуть чрезвычайно трудная для поиска ошибка.

assert falseПредложение хорошее, но так как утверждения могут быть отключены во время выполнения, они не являются гарантией. Я бы использовал RuntimeExceptionвместо этого. Их не нужно будет отлавливать при вызове классов, и если они когда-либо произойдут, у вас будет трассировка стека, дающая полную информацию.

Фредрик
источник
28

Я всегда делал это так:

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

Может быть немного многословно (Java ...), но, по крайней мере, вы получите ошибку утверждения, когда произойдет невозможное.

Если реализация Java нарушена, вы захотите получить как можно более качественное сообщение об ошибке как можно быстрее, вместо того, чтобы просто игнорировать невозможное. И даже если реализация Java не нарушена, кто-то мог бы изменить ваш код на "UTF8"(упс, должно ли это быть "UTF-8"?).

Во -первых, это должно быть исключение времени выполнения. JDK полон такого неправильного выбора.

Joonas Pulakka
источник
4
Очень плохое решение (или, если хотите, упущение) состоит в том, что нет предопределенных экземпляров Charset для тех 6 наборов символов, которые требуются Java. foobar = new InputStreamReader(p.getInputStream(), Charset.ISO_8859_1);- разве это не было бы лучше и не допустить ошибки раз и навсегда?
user281377 29.11.11
3
В библиотеке Гуавы есть множество таких вещей. Делает жизнь проще.
3
В Java 1.7+ определены константы кодировки: docs.oracle.com/javase/7/docs/api/java/nio/charset/… . Используйте их так: StandardCharsets.UTF_8.displayName ()
Майкл
4

Если вы являетесь единственным разработчиком, который когда-либо увидит этот код, то я бы сказал, что это нормально, но если нет, то я бы отнесся к нему как к реальной возможности или, по крайней мере, изменил бы комментарий «никогда не случится» на что-то более полезное.

Танос Папатанасиу
источник
4

Больше всего меня раздражает то, что это вредит моему покрытию кода.

Когда я получу навязчивую информацию об освещении, я сверну попытку / улов, который "никогда не случится" (... или только если я использую мутантную JVM, которая почему-то забыла включить "US-ASCII") в класс и метод, который инкапсулирует этот try / catch и заменяет проверенное исключение одним из способов, упомянутых здесь (обычно выбрасывает непроверенное исключение с помощью сообщения об ошибке).

Тогда мое покрытие кода получает удар в служебном классе, но не во всех ссылках на эту операцию, разбросанных по моему коду.

Иногда я нахожу время, чтобы свернуть как операции в классе, который на самом деле имеет согласованную семантику. Но так как для моих товарищей по команде совершенно очевидно, что происходит, я обычно просто стараюсь делать это как можно проще и не беспокоиться о наилучшем возможном дизайне.

Однако, как упоминалось в комментарии, у Guava и других библиотек есть способы смягчить эту боль - но это в основном та же стратегия. Переместите раздражение за сцену, чтобы ваш основной код не принял удар по освещению.

обкрадывать
источник