Дело против проверенных исключений

Уже несколько лет я не могу получить достойный ответ на следующий вопрос: почему некоторые разработчики так против проверенных исключений? У меня было много разговоров, читал что-то в блогах, читал то, что говорил Брюс Экель (первый человек, которого я видел, выступал против них).

В настоящее время я пишу новый код и уделяю очень пристальное внимание тому, как я работаю с исключениями. Я пытаюсь увидеть точку зрения толпы «нам не нравятся проверенные исключения», и я до сих пор ее не вижу.

Каждый мой разговор заканчивается тем, что один и тот же вопрос остается без ответа ... позвольте мне настроить его:

В целом (из того, как был разработан Java),

  • Error для вещей, которые никогда не должны быть пойманы (В.М. имеет аллергию на арахис, и кто-то бросил банку с арахисом на него)
  • RuntimeException для вещей, которые программист сделал неправильно (программист вышел из конца массива)
  • Exception(кроме RuntimeException) для вещей, которые находятся вне контроля программиста (диск заполняется при записи в файловую систему, достигнут предел дескриптора файла для процесса, и вы больше не можете открывать файлы)
  • Throwable просто родитель всех типов исключений.

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

Еще один распространенный аргумент, который я слышу, заключается в том, что проверенные исключения затрудняют рефакторинг кода.

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

Для толпы «это затрудняет рефакторинг», это указывает на то, что правильный уровень абстракции не был выбран. Вместо того чтобы объявлять метод, который выдает метод IOException, IOExceptionследует преобразовать исключение, которое больше подходит для происходящего.

У меня нет проблем с переносом Main в catch(Exception)(или в некоторых случаях, catch(Throwable)чтобы программа могла корректно завершить работу - но я всегда ловлю конкретные исключения, которые мне нужны. Это позволяет мне, по крайней мере, отображать соответствующие сообщение об ошибке.

Вопрос, на который люди никогда не отвечают, заключается в следующем:

Если вы бросаете RuntimeException подклассы вместо Exception подклассов, то как вы узнаете, что вы должны ловить?

Если ответ «улов», Exceptionто вы также имеете дело с ошибками программиста так же, как системные исключения. Это кажется мне неправильным.

Если вы перехватываете, Throwableвы обрабатываете системные исключения и ошибки ВМ (и тому подобное) одинаково. Это кажется мне неправильным.

Если ответ таков, что вы ловите только те исключения, которые, как вы знаете, выбрасываются, то как вы узнаете, какие из них выбрасываются? Что происходит, когда программист X выдает новое исключение и забывает его перехватить? Это кажется мне очень опасным.

Я бы сказал, что программа, отображающая трассировку стека, неверна. Люди, которым не нравятся проверенные исключения, не чувствуют себя так?

Итак, если вам не нравятся отмеченные исключения, можете ли вы объяснить, почему нет И ответьте на вопрос, на который нет ответа, пожалуйста?

Редактировать: я не ищу советов о том, когда использовать какую-либо модель, я ищу то, почему люди расширяются, RuntimeExceptionпотому что им не нравится расширяться Exceptionи / или почему они ловят исключение, а затем отбрасывают, RuntimeExceptionа не добавляют броски в их метод. Я хочу понять мотивацию неприязни к проверенным исключениям.

5.03.2009 08:18:54
Я не думаю, что это полностью субъективно - это языковая функция, которая была разработана для конкретного использования, а не для того, чтобы каждый сам решал, для чего он нужен. И это не особенно аргументировано, в нем заранее рассматриваются конкретные опровержения, которые люди могли легко придумать.
Gareth 5.03.2009 08:49:00
Давай. Рассматриваемая как языковая особенность, эта тема была и может подходить объективно.
Kurt Schelfthout 5.03.2009 12:30:52
@cletus «отвечая на свой вопрос», если бы у меня был ответ, я бы не задавал вопрос!
TofuBeer 5.03.2009 15:32:49
Отличный вопрос В C ++ нет проверенных исключений вообще, и, на мой взгляд, это делает функцию исключений непригодной. Вы попадаете в ситуацию, когда вам приходится ловить каждый вызов функции, который вы делаете, потому что вы просто не знаете, может ли он что-то генерировать.
Dimitri C. 24.06.2009 10:15:49
C ++ не работает без проверенных исключений IMO - как часто вы видите catch (...) просто для того, чтобы убедиться, что ничего не происходит. Итак, поскольку у вас большая кодовая база, пожалуйста, ответьте на простой вопрос - как убедиться, что вы не пропустите ни одного исключения? Скажем, вы добавляете новое «FooException» - как вы гарантируете, что ваша программа не аварийно завершит работу, если вам не удастся ее перехватить? Как вы ловите это во всех правильных местах?
TofuBeer 18.03.2010 20:19:38
30 ОТВЕТОВ

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

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

И причина в том, что, если это так, я должен это исправить, и я должен исправить это сразу. Я хочу сразу знать, что есть проблема.

Сколько раз вы на самом деле обрабатываете исключения? Я не говорю о ловле исключений - я говорю об обработке их? Слишком легко написать следующее:

try {
  thirdPartyMethod();
} catch(TPException e) {
  // this should never happen
}

И я знаю, что вы можете сказать, что это плохая практика, и что «ответ» заключается в том, чтобы сделать что-то с исключением (позвольте мне угадать, записать это?), Но в реальном мире (тм) большинство программистов просто не делают Это.

Так что да, я не хочу ловить исключения, если мне не нужно это делать, и я хочу, чтобы моя программа эффектно взорвалась, когда я облажался. Молчаливая неудача - худший из возможных результатов.

20
5.03.2009 09:07:15
Java рекомендует вам делать такие вещи, чтобы вам не приходилось добавлять исключения каждого типа в каждую сигнатуру метода.
yfeldblum 5.03.2009 12:00:21
Забавно ... с тех пор, как я правильно обнял проверенные исключения и использовал их должным образом, мои программы перестали взрываться в огромной дымящейся куче недовольных клиентов. Если во время разработки у вас будет большой уродливый плохой след стека, то клиент обязательно получит их. Хочу увидеть его лицо, когда он видит ArrayIndexOutOfBoundsException с трассировкой стека высотой в милю в своей сбойной системе вместо небольшого уведомления в трее, говорящего о том, что цветовая конфигурация для кнопки XYZ не может быть проанализирована, поэтому вместо этого использовалось программное обеспечение по умолчанию, а программное обеспечение счастливо гудело вместе
Newtopian 7.08.2009 16:46:58
Возможно, то, что нужно Java, - это объявление «cantHandle», которое указывало бы, что метод или блок кода try / catch не подготовлен для обработки конкретного исключения, которое происходит внутри него, и что любое такое исключение, которое происходит с помощью средств, отличных от явного throw в этом методе (в отличие от вызываемого метода) должен автоматически переноситься и перебрасываться в RuntimeException. ИМХО, проверенные исключения должны редко распространяться вверх по стеку вызовов без переноса.
supercat 24.01.2013 23:52:34
@Newtopian - я пишу серверное и высоконадежное программное обеспечение и занимаюсь этим уже 25 лет. Мои программы никогда не взрывались, и я работаю с финансовыми и военными системами с высокой степенью готовности, повторной попытки и повторного подключения. У меня есть абсолютная объективная основа, чтобы предпочесть исключения во время выполнения. Проверенные исключения затрудняют правильную практику «брось рано, поймай поздно». Надежная надежность и обработка ошибок находятся на уровне «бизнес», «соединение» или «запрос». (Или иногда при разборе данных). Проверенные исключения мешают делать это правильно.
Thomas W 2.06.2014 02:39:11
Исключения, о которых вы здесь говорите, это то, RuntimeExceptionsчто вам действительно не нужно ловить, и я согласен, что вы должны позволить программе взорваться. Исключения, которые вы всегда должны ловить и обрабатывать, - это проверенные исключения IOException. Если вы получите IOException, в вашем коде нечего исправить; Ваша программа не должна взорваться только потому, что произошел сбой сети.
AxiomaticNexus 25.06.2014 21:25:59

Вот один аргумент против проверенных исключений (от joelonsoftware.com):

Причина в том, что я считаю исключения не лучше, чем «goto», считающиеся вредными с 1960-х годов, в том смысле, что они создают резкий переход от одной точки кода к другой. На самом деле они значительно хуже, чем у Гото:

  • Они невидимы в исходном коде. Глядя на блок кода, включая функции, которые могут генерировать или не генерировать исключения, нет никакого способа увидеть, какие исключения могут быть выброшены и откуда. Это означает, что даже тщательная проверка кода не выявляет потенциальных ошибок.
  • Они создают слишком много возможных точек выхода для функции. Чтобы написать правильный код, вам действительно нужно подумать о каждом возможном пути кода в вашей функции. Каждый раз, когда вы вызываете функцию, которая может вызвать исключение и не перехватить его на месте, вы создаете возможности для неожиданных ошибок, вызванных внезапно завершившимися функциями, оставив данные в несогласованном состоянии или другими путями кода, которые вы не сделали. подумать о.
4
7.03.2013 00:48:59
+1 Возможно, вы захотите обобщить аргумент в вашем ответе, хотя? Они похожи на невидимых gotos и ранних выходов для ваших процедур, разбросанных по всей программе.
MarkJ 5.03.2009 09:20:48
Это больше аргумент против исключений в целом.
Ionuț G. Stan 5.03.2009 09:36:55
Вы действительно читали статью! Во-первых, он говорит об исключениях в целом, во-вторых, раздел «Они невидимы в исходном коде» относится конкретно к исключению UNCHECKED. В этом весь смысл проверенного исключения ... так что вы ЗНАЕТЕ, какой код выдает, что куда
Newtopian 7.08.2009 16:55:43
@ Ева Они не одинаковы. С оператором goto вы можете увидеть gotoключевое слово. С помощью цикла вы можете увидеть закрывающую фигурную скобку breakили continueключевое слово or . Все они переходят к точке текущего метода. Но вы не можете всегда видеть throw, потому что часто это не в текущем методе , но в другом методе , который он называет (возможно , косвенно.)
finnw 7.03.2013 00:45:19
@finnw Функции сами по себе являются формой перехода. Вы обычно не знаете, какие функции вызывают функции, которые вы вызываете. Если вы программируете без функций, у вас не будет проблем с невидимыми исключениями. Это означает, что проблема не связана конкретно с исключениями и не является допустимым аргументом против исключений в целом. Можно сказать, что коды ошибок быстрее, можно сказать, что монады чище, но аргумент goto глуп.
Eva 7.03.2013 01:15:25

За последние три года я работал с несколькими разработчиками в относительно сложных приложениях. У нас есть кодовая база, которая довольно часто использует Checked Exceptions с правильной обработкой ошибок, а другая - нет.

До сих пор мне было проще работать с базой кода с проверенными исключениями. Когда я использую чужой API, приятно видеть, какие именно ошибки можно ожидать, когда я вызываю код и обрабатываю их должным образом, путем регистрации, отображения или игнорирования (да, есть допустимые случаи игнорирования исключения, такие как реализация ClassLoader). Это дает код, который я пишу, возможность восстановить. Все исключения во время выполнения я распространяю до тех пор, пока они не будут кэшированы и обработаны с помощью некоторого общего кода обработки ошибок. Когда я нахожу проверенное исключение, которое на самом деле не хочу обрабатывать на определенном уровне или которое я рассматриваю как ошибку логики программирования, я оборачиваю его в RuntimeException и позволяю ему всплыть. Никогда, никогда не глотайте исключение без уважительной причины (а веских причин для этого довольно мало)

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

Это все, конечно, вопрос предпочтений и навыков разработчика. Оба способа программирования и обработки ошибок могут быть одинаково эффективными (или неэффективными), поэтому я бы не сказал, что существует The One Way.

В общем, мне легче работать с проверенными исключениями, особенно в больших проектах с большим количеством разработчиков.

16
5.03.2009 10:00:24
Я делаю, чтобы Для меня они являются неотъемлемой частью контракта. Не прибегая к подробностям документации API, я быстро узнаю наиболее вероятные сценарии ошибок.
Wayne Hartman 18.05.2009 03:47:24
Согласен. Однажды я испытал необходимость проверенных исключений в .Net, когда пытался совершать сетевые вызовы. Зная, что сбой в сети может произойти в любой момент, мне пришлось прочитать всю документацию API, чтобы выяснить, какое исключение мне нужно было отловить специально для этого сценария. Если бы C # проверил исключения, я бы знал об этом немедленно. Другие разработчики C #, вероятно, просто допустили бы сбой приложения из-за простой сетевой ошибки.
AxiomaticNexus 25.06.2014 21:35:51

Ну, речь не идет об отображении трассировки стека или тихом сбое. Это возможность сообщать об ошибках между слоями.

Проблема с проверенными исключениями состоит в том, что они побуждают людей проглотить важные детали (а именно, класс исключений). Если вы решите не проглатывать эту деталь, вам придется добавлять объявления бросков по всему вашему приложению. Это означает, 1) что новый тип исключения будет влиять на множество сигнатур функций, и 2) вы можете пропустить конкретный экземпляр исключения, которое вы на самом деле хотите поймать (скажем, вы открываете дополнительный файл для функции, которая записывает данные в файл. Вторичный файл является необязательным, поэтому вы можете игнорировать его ошибки, но поскольку подпись throws IOException, это легко пропустить).

Я на самом деле имею дело с этой ситуацией сейчас в приложении. Мы перепаковали почти исключения как AppSpecificException. Это сделало подписи действительно чистыми, и нам не пришлось беспокоиться о взрывах throwsподписей.

Конечно, теперь нам нужно специализировать обработку ошибок на более высоких уровнях, реализуя логику повторных попыток и тому подобное. Все это AppSpecificException, поэтому мы не можем сказать «Если IOException выброшен, повторите попытку» или «Если ClassNotFound выброшен, прервать полностью». У нас нет надежного способа получения в режиме реального исключения , потому что все становится упакованного снова и снова , как они проходят между нашим кодом и кодом третьей стороной.

Вот почему я большой поклонник обработки исключений в Python. Вы можете поймать только то, что вы хотите и / или можете справиться. Все остальное пузырится, как будто вы перебрасываете это самостоятельно (что вы сделали в любом случае).

Я снова и снова обнаруживал, что в течение всего проекта, который я упоминал, обработка исключений делится на 3 категории:

  1. Поймать и обработать конкретное исключение. Это для реализации логики повторения, например.
  2. Поймай и отбрось другие исключения. Все, что здесь происходит, это, как правило, ведение журнала, и обычно это банальное сообщение типа «Невозможно открыть $ filename». Это ошибки, с которыми ничего не поделаешь; только более высокие уровни знают достаточно, чтобы справиться с этим.
  3. Поймай все и покажи сообщение об ошибке. Обычно это находится в самом корне диспетчера, и все, что он делает, это гарантирует, что он может сообщить об ошибке вызывающей стороне через механизм не-исключения (всплывающий диалог, маршалинг объекта RPC-ошибки и т. Д.).
45
7.08.2009 17:53:57
Вы могли бы сделать определенные подклассы AppSpecificException, чтобы разрешить разделение, сохраняя при этом простые сигнатуры методов.
Thorbjørn Ravn Andersen 1.11.2009 08:21:52
Также очень важным дополнением к пункту 2 является то, что он позволяет ДОБАВИТЬ ИНФОРМАЦИЮ в обнаруженное исключение (например, путем вложения в RuntimeException). Намного лучше иметь имя файла, не найденного в трассировке стека, чем скрытое глубоко в файле журнала.
Thorbjørn Ravn Andersen 1.11.2009 08:25:07
По сути, ваш аргумент «Управление исключениями утомляет, поэтому я бы предпочел не заниматься этим». Поскольку исключение всплывает, оно теряет смысл, а создание контекста практически бесполезно. Как разработчик API, вы должны четко договориться о том, что можно ожидать, когда что-то пойдет не так, если моя программа падает, потому что я не был проинформирован о том, что то или иное исключение может «всплыть», тогда вы, как дизайнер, потерпели неудачу и как В результате вашего отказа моя система не так стабильна, как могла бы быть.
Newtopian 8.11.2009 04:07:52
Это совсем не то, что я говорю. Ваше последнее предложение на самом деле согласен со мной. Если все обернуто в AppSpecificException, то оно не всплывает (и смысл / контекст теряется), и, да, клиент API не информируется - это именно то, что происходит с проверенными исключениями (как в Java) , потому что люди не хотят иметь дело с функциями с большим количеством throwsобъявлений.
Richard Levasseur 8.11.2009 06:33:31
@Newtopian - исключения могут в основном обрабатываться только на уровне «бизнес» или «запрос». Имеет смысл потерпеть неудачу или повторить попытку с большой степенью детализации, а не для каждого крошечного потенциального сбоя. По этой причине передовая практика обработки исключений обобщается как «бросай рано, лови поздно». Проверенные исключения усложняют управление надежностью на правильном уровне и поощряют огромное количество неправильно кодированных блоков улова. literatejava.com/exceptions/…
Thomas W 2.06.2014 02:09:46

Я думаю, что это отличный вопрос, а не спорный. Я думаю, что сторонние библиотеки должны (в общем) генерировать непроверенные исключения. Это означает, что вы можете изолировать свои зависимости от библиотеки (то есть вам не нужно ни перебрасывать их исключения, ни выбрасывать Exception- обычно это плохая практика). Слой Spring DAO является отличным примером этого.

С другой стороны, исключения из основного Java API, как правило, следует проверять, можно ли их когда-либо обрабатывать. Возьми FileNotFoundExceptionили (мой любимый) InterruptedException. Эти условия почти всегда должны рассматриваться конкретно (т.е. ваша реакция на an InterruptedExceptionне совпадает с вашей реакцией на an IllegalArgumentException). Тот факт, что ваши исключения проверены, заставляет разработчиков задуматься о том, является ли условие пригодным для обработки или нет. (Тем не менее, я редко видел, как InterruptedExceptionобрабатываются правильно!)

Еще одна вещь - это RuntimeExceptionне всегда «где разработчик что-то не так». Исключение недопустимого аргумента выдается, когда вы пытаетесь создать enumиспользование, valueOfи enumэтого имени нет. Это не обязательно ошибка разработчика!

2
5.03.2009 10:00:40
Да, это ошибка разработчика. Они явно не использовали правильное имя, поэтому они должны вернуться и исправить свой код.
AxiomaticNexus 25.06.2014 21:41:35
@AxiomaticNexus Ни один здравомыслящий разработчик не использует enumимена членов просто потому, что enumвместо них они используют объекты. Таким образом, неправильное имя может быть получено извне, будь то файл импорта или что-то еще. Один из возможных способов справиться с такими именами - звонить MyEnum#valueOfи ловить IAE. Другой способ - использовать предварительно заполненные Map<String, MyEnum>, но это детали реализации.
maaartinus 2.12.2017 15:54:07
@maaartinus В некоторых случаях имена членов перечисления используются без внешней строки. Например, когда вы хотите, чтобы динамически проходить через все элементы, чтобы что-то делать с каждым. Кроме того, неважно, пришла ли строка извне или нет. Разработчик обладает всей необходимой информацией, чтобы узнать, приведет ли передача строки x к «MyEnum # valueOf», перед передачей ошибки. Передача x строки в «MyEnum # valueOf» в любом случае, когда это вызвало бы ошибку, явно была бы ошибкой со стороны разработчика.
AxiomaticNexus 4.12.2017 18:52:23

Андерс говорит о подводных камнях проверенных исключений и о том, почему он исключил их из C # в эпизоде ​​97 радио Software Engineering.

5
5.03.2009 10:02:53

Artima опубликовала интервью с одним из архитекторов .NET, Андерсом Хейлсбергом, в котором подробно освещены аргументы против проверенных исключений. Короткий дегустатор:

Предложение throws, по крайней мере так, как это реализовано в Java, не обязательно заставляет вас обрабатывать исключения, но если вы не обрабатываете их, оно заставляет вас точно знать, через какие исключения они могут пройти. Это требует, чтобы вы либо ловили объявленные исключения, либо помещали их в свое собственное предложение throws. Чтобы обойти это требование, люди делают смешные вещи. Например, они украшают каждый метод с помощью «throws Exception». Это просто полностью побеждает эту особенность, и вы просто заставили программиста писать все больше и больше. Это никому не поможет.

22
5.03.2009 10:34:58
На самом деле главный архитектор.
Trap 5.03.2009 12:11:04
Я читал, что его аргумент сводится к тому, что «там плохие программисты».
TofuBeer 5.03.2009 15:11:21
TofuBeer, совсем нет. Все дело в том, что часто вы не знаете, что делать с Исключением, которое выдает вызываемый метод, и случай, который вас действительно интересует, даже не упоминается. Вы открываете файл, вы получаете IO Exception, например ... это не моя проблема, поэтому я выбрасываю его. Но вызывающий метод верхнего уровня просто захочет остановить обработку и сообщить пользователю, что существует неизвестная проблема. Проверенное исключение совсем не помогло. Это была одна из миллиона странных вещей, которые могут случиться.
Dan Rosenstark 27.05.2009 12:19:48
@yar, если вам не нравится проверенное исключение, то создайте «throw new RuntimeException» (мы не ожидали этого при выполнении Foo.bar () «, e)» и покончим с этим.
Thorbjørn Ravn Andersen 1.11.2009 09:32:10
@ ThorbjørnRavnAndersen: фундаментальный недостаток дизайна в Java, который, к сожалению, скопирован .net, заключается в том, что он использует тип исключения в качестве основного средства для принятия решения, следует ли действовать, и основного средства указания общего типа вещи. что пошло не так, когда на самом деле эти две проблемы в значительной степени ортогональны. Важно не то, что пошло не так, а то, в чем состоят объекты состояния. Кроме того, и .net, и Java по умолчанию предполагают, что действие и устранение исключения, как правило, одно и то же, хотя на самом деле они часто разные.
supercat 29.10.2012 22:20:03

Короче говоря:

Исключения составляют вопрос разработки API. -- Не больше, не меньше.

Аргумент для проверенных исключений:

Чтобы понять, почему проверенные исключения могут быть не очень хорошими вещами, давайте перевернем вопрос и спросим: когда или почему проверенные исключения привлекательны, то есть, почему вы хотите, чтобы компилятор принудительно применял объявление исключений?

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

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

В действительности, однако, слишком часто пакет com.acmeтолько бросает, AcmeExceptionа не определенные подклассы. Затем вызывающим абонентам необходимо обрабатывать, объявлять или повторно сигнализировать AcmeExceptions, но они все еще не могут быть уверены, AcmeFileNotFoundErrorпроизошло ли это или произошло AcmePermissionDeniedError.

Поэтому, если вы заинтересованы только в AcmeFileNotFoundErrorрешении, решение состоит в том, чтобы подать запрос функции программистам ACME и сказать им, чтобы они реализовали, объявили и документировали этот подкласс AcmeException.

Так зачем?

Следовательно, даже с проверенными исключениями компилятор не может заставить программистов создавать полезные исключения. Это все еще вопрос качества API.

В результате языки без проверенных исключений обычно не намного хуже. Программисты могут испытывать искушение бросать неопределенные экземпляры общего Errorкласса, а не класса AcmeException, но если они вообще заботятся о качестве своего API, они все равно научатся вводить AcmeFileNotFoundError.

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

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

Стоит также отметить, что Java VM не имеет проверенных исключений - только их проверяет компилятор Java, а файлы классов с измененными объявлениями исключений совместимы во время выполнения. Безопасность Java VM не улучшена проверенными исключениями, только стиль кодирования.

19
5.03.2009 10:40:04
Ваш аргумент противоречит самому себе. Если «иногда вам нужно поймать исключение» и качество API часто плохое, без проверенных исключений вы не будете знать, не забыл ли разработчик задокументировать, что определенный метод выдает исключение, которое необходимо перехватить. Соедините это с броском, AcmeExceptionа не с AcmeFileNotFoundErrorудачей, выясняя, что вы сделали неправильно, и где вам нужно поймать это. Проверенные исключения предоставляют программистам некоторую защиту от плохого дизайна API.
Eva 28.02.2013 16:27:20
Дизайн библиотеки Java допустил серьезные ошибки. «Проверенные исключения» предназначались для предсказуемых и восстанавливаемых непредвиденных обстоятельств - таких как файл не найден, сбой соединения. Они никогда не предназначались или не подходили для системного сбоя низкого уровня. Было бы неплохо принудительно проверить открытие файла, но нет разумной повторной попытки или восстановления из-за невозможности записать один байт / выполнить SQL-запрос и т. Д. Повторная попытка или восстановление корректно обрабатываются по запросу «business» или «request». Уровень, на котором проверяются исключения, делать бессмысленно сложно. literatejava.com/exceptions/...
Thomas W 2.06.2014 02:14:45

Суть проверяемых исключений в том, что они не являются исключениями при обычном понимании концепции. Вместо этого они являются альтернативными возвращаемыми значениями API.

Вся идея исключений заключается в том, что ошибка, выдаваемая где-то далеко вниз по цепочке вызовов, может всплывать и обрабатываться кодом где-то дальше, без вмешательства кода. Проверенные исключения, с другой стороны, требуют, чтобы каждый уровень кода между метателем и перехватчиком объявлял, что они знают обо всех формах исключения, которые могут пройти через них. На практике это мало чем отличается от того, что проверяемые исключения были просто специальными возвращаемыми значениями, которые должен был проверять вызывающий объект. например, [псевдокод].:

public [int or IOException] writeToStream(OutputStream stream) {
    [void or IOException] a= stream.write(mybytes);
    if (a instanceof IOException)
        return a;
    return mybytes.length;
}

Поскольку Java не может использовать альтернативные возвращаемые значения или простые встроенные кортежи в качестве возвращаемых значений, проверенные исключения являются разумным ответом.

Проблема в том, что большая часть кода, в том числе отличные образцы стандартной библиотеки, неправильно использует проверенные исключения для реальных исключительных условий, которые вы вполне можете захотеть поднять на несколько уровней. Почему IOException не является RuntimeException? На любом другом языке я могу допустить исключение ввода-вывода, и если я ничего не сделаю для его обработки, мое приложение остановится, и я получу удобную трассировку стека для просмотра. Это лучшее, что может случиться.

Возможно, двумя способами из примера вы хотите перехватить все IOException от всего процесса записи в поток, прервать процесс и перейти к коду сообщения об ошибках; в Java вы не можете сделать это без добавления «throws IOException» на каждом уровне вызова, даже на уровнях, которые сами по себе не выполняют IO. Такие методы не должны знать об обработке исключений; добавление исключений в их подписи:

  1. излишне увеличивает сцепление;
  2. делает подписи интерфейса очень хрупкими для изменения;
  3. делает код менее читаемым;
  4. Это настолько раздражает, что обычная реакция программиста состоит в том, чтобы победить систему, выполнив что-то ужасное, например, «throws Exception», «catch (Exception e) {}», или завернуть все в RuntimeException (что затрудняет отладку).

И еще есть множество просто смешных библиотечных исключений, таких как:

try {
    httpconn.setRequestMethod("POST");
} catch (ProtocolException e) {
    throw new CanNeverHappenException("oh dear!");
}

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

Другой конкретный плохой эффект связан с инверсией управления, когда компонент A передает обратный вызов универсальному компоненту B. Компонент A хочет иметь возможность разрешить исключение из своего обратного вызова обратно в то место, где он вызвал компонент B, но не может потому что это изменило бы интерфейс обратного вызова, который исправлен B. A может сделать это, только обернув реальное исключение в RuntimeException, который является еще одним шаблоном обработки исключений для записи.

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

182
17.02.2015 08:17:30
В вашем примере кода было бы лучше объединить исключения, чтобы можно было найти исходную причину при чтении журналов: throw CanNeverHappenException (e);
Esko Luontola 6.03.2009 11:25:41
Я не согласен. Исключения, проверенные или нет, являются исключительными условиями. Пример: метод, который извлекает объект через HTTP. Возвращаемое значение - это еще один объект или ничего, все вещи, которые могут испортиться, являются исключительными. Обработка их как возвращаемых значений, как это делается в C, приводит только к путанице и плохому дизайну.
Mister Smith 22.08.2011 11:33:48
@Mister: Я хочу сказать, что проверенные исключения, реализованные в Java, на практике ведут себя больше как возвращаемые значения, как в C, чем как традиционные «исключения», которые мы могли бы узнать из C ++ и других языков до Java. И это IMO действительно приводит к путанице и плохому дизайну.
bobince 22.08.2011 13:23:37
Согласитесь, что в стандартных библиотеках неправильное использование проверенных исключений определенно усугубляет путаницу и плохую ловушку. И часто это просто из-за плохой документации, например, метода разрыва, такого как disconnect (), который выдает IOException, когда «происходит какая-то другая ошибка ввода-вывода». Ну, я отключился! Я протекаю ручку или другой ресурс? Мне нужно повторить попытку? Не зная, почему это произошло, я не могу получить действие, которое я должен предпринять, и поэтому я должен догадаться, должен ли я просто проглотить это, повторить попытку или поручиться.
charstar 3.12.2011 09:26:55
+1 за «альтернативные возвращаемые значения API». Интересный взгляд на проверенные исключения.
Zsolt Török 20.12.2011 12:38:03

Вместо того, чтобы перефразировать все (многие) причины против проверенных исключений, я выберу только одну. Я потерял счетчик количества написаний этого блока кода:

try {
  // do stuff
} catch (AnnoyingcheckedException e) {
  throw new RuntimeException(e);
}

99% времени я ничего не могу с этим поделать. Наконец блоки делают любую необходимую очистку (или, по крайней мере, они должны).

Я также потерял счет количество раз, я видел это:

try {
  // do stuff
} catch (AnnoyingCheckedException e) {
  // do nothing
}

Почему? Потому что кому-то приходилось иметь с этим дело и было лень. Было ли это неправильно? Конечно. Это происходит? Абсолютно. Что если бы это было неконтролируемое исключение? Приложение только что умерло (что предпочтительнее, чем проглатывание исключения).

И затем у нас есть бешеный код, который использует исключения как форму управления потоком, как это делает java.text.Format . Bzzzt. Неправильно. Пользователь, помещающий «abc» в числовое поле формы, не является исключением.

Хорошо, я думаю, это было три причины.

76
5.03.2009 11:44:29
Но если исключение правильно отловлено, вы можете сообщить пользователю, выполнить другие задачи (войти?) И выйти из приложения контролируемым образом. Я согласен, что некоторые части API могли бы быть разработаны лучше. И по причине ленивого программиста, я думаю, как программист, вы на 100% ответственны за свой код.
Mister Smith 23.08.2011 06:22:48
обратите внимание, что try-catch-rethrow позволяет вам указать сообщение - я обычно использую его для добавления информации о содержимом переменных состояния. Частым примером является исключение IOException для добавления absolutePathName () рассматриваемого файла.
Thorbjørn Ravn Andersen 29.10.2012 22:34:49
Я думаю, что IDE, такие как Eclipse, могут винить в том, сколько раз вы видели пустой блок catch. Действительно, они должны перебрасывать по умолчанию.
artbristol 8.11.2013 11:10:39
«99% времени я ничего не могу с этим поделать» - неправильно, вы можете показать пользователю сообщение «Не удалось подключиться к серверу» или «Произошел сбой устройства ввода-вывода» вместо того, чтобы просто дать приложению аварийно завершиться. из-за небольшого сбоя в сети. Оба ваших примера являются искусством работы плохих программистов. Вы должны атаковать плохих программистов и сами не проверять исключения. Это похоже на то, что я нападаю на инсулин за то, что я не помогаю с диабетом, когда я использую его как заправку для салата.
AxiomaticNexus 25.06.2014 21:56:35
@YasmaniLlanes Вы не можете всегда делать эти вещи. Иногда у вас есть интерфейс, чтобы придерживаться. И это особенно верно, когда вы разрабатываете хорошие поддерживаемые API, потому что вы не можете просто начать создавать побочные эффекты повсюду. И это, и сложность, которую он представит, сильно укусят вас в больших масштабах. Так что да, в 99% случаев с этим ничего не поделаешь.
MasterMastic 25.08.2015 20:07:49

Я думаю, что прочитал то же интервью с Брюсом Экелом, что и вы, и оно всегда меня раздражало. Фактически, аргумент был сделан интервьюируемым (если это действительно тот пост, о котором вы говорите) Андерсом Хейлсбергом, гением MS, стоящим за .NET и C #.

http://www.artima.com/intv/handcuffs.html

Несмотря на то, что я из Хейлсберга и его работ, этот аргумент всегда казался мне фальшивым. Это в основном сводится к:

«Проверенные исключения являются плохими, потому что программисты просто злоупотребляют ими, всегда ловя их и игнорируя их, что приводит к скрытым и игнорируемым проблемам, которые иначе были бы представлены пользователю».

Под «иным способом представляемым пользователю» я подразумеваю, что если вы используете исключение времени выполнения, ленивый программист просто проигнорирует его (вместо того, чтобы перехватить его пустым блоком catch), и пользователь увидит его.

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

В этом аргументе есть доля правды, и на самом деле, я подозреваю, что мотивация Гослинга не помещать переопределения операторов в Java исходит из аналогичного аргумента - они вводят программиста в заблуждение, потому что им часто злоупотребляют.

Но, в конце концов, я нахожу это фиктивным аргументом Хейлсберга и, возможно, постфактумным аргументом, созданным для объяснения недостатка, а не продуманным решением.

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

Теперь программист API должен быть осторожен, чтобы не создавать проверенные исключения повсеместно, иначе они будут просто раздражать программиста клиента. (Exception) {}Хейлсберг предупреждает, что очень ленивый клиент-программист прибегнет к ловле, и вся польза будет потеряна, и наступит ад. Но в некоторых обстоятельствах просто нет замены хорошему проверенному исключению.

Для меня классическим примером является API открытия файлов. Каждый язык программирования в истории языков (по крайней мере, в файловых системах) имеет где-то API, который позволяет открывать файл. И каждый клиентский программист, использующий этот API, знает, что им приходится иметь дело с тем случаем, что файл, который они пытаются открыть, не существует. Позвольте мне перефразировать это: каждый клиентский программист, использующий этот API, должен знать, что он должен иметь дело с этим делом. И тут есть одна загвоздка: могут ли программисты API помочь им узнать, что им следует справиться с этим, оставив только комментарии, или они действительно настаивают, чтобы клиент справился с этим.

В Си идиома идет что-то вроде

  if (f = fopen("goodluckfindingthisfile")) { ... } 
  else { // file not found ...

где fopenуказывает на ошибку, возвращая 0, а C (по глупости) позволяет вам рассматривать 0 как логическое значение и ... В основном, вы изучаете эту идиому и все в порядке. Но что, если вы новичок и не выучили идиому. Тогда, конечно, вы начинаете с

   f = fopen("goodluckfindingthisfile");
   f.read(); // BANG! 

и учиться трудному пути.

Обратите внимание, что здесь речь идет только о строго типизированных языках: есть четкое представление о том, что такое API в строго типизированном языке: это шведский стол функциональности (методов), который вы можете использовать с четко определенным протоколом для каждого из них.

Этот четко определенный протокол обычно определяется сигнатурой метода. Здесь fopen требует, чтобы вы передали ему строку (или символ * в случае C). Если вы дадите ему что-то еще, вы получите ошибку во время компиляции. Вы не следовали протоколу - вы не используете API должным образом.

В некоторых (малоизвестных) языках тип возвращаемого значения также является частью протокола. Если вы попытаетесь вызвать эквивалент fopen()в некоторых языках без присвоения его переменной, вы также получите ошибку во время компиляции (вы можете сделать это только с помощью функций void).

Я пытаюсь подчеркнуть следующее: на языке статической типизации программист API рекомендует клиенту правильно использовать API, предотвращая компиляцию клиентского кода, если он допускает какие-либо очевидные ошибки.

(В динамически типизированном языке, таком как Ruby, вы можете передать в качестве имени файла что угодно, скажем, float, и он скомпилируется. Зачем беспокоить пользователя проверенными исключениями, если вы даже не собираетесь контролировать аргументы метода. приводимые здесь аргументы применимы только к статически типизированным языкам.)

Итак, что насчет проверенных исключений?

Вот один из API-интерфейсов Java, который вы можете использовать для открытия файла.

try {
  f = new FileInputStream("goodluckfindingthisfile");
}
catch (FileNotFoundException e) {
  // deal with it. No really, deal with it!
  ... // this is me dealing with it
}

Видишь этот улов? Вот подпись для этого метода API:

public FileInputStream(String name)
                throws FileNotFoundException

Обратите внимание, что FileNotFoundExceptionэто проверенное исключение.

Программист API говорит вам следующее: «Вы можете использовать этот конструктор для создания нового FileInputStream, но вы

а) должен передать имя файла в виде строки
б) должен принять возможность того, что файл может быть не найден во время выполнения "

И это все, что касается меня.

Ключ в основном состоит в том, что этот вопрос гласит как «Вещи, которые находятся вне контроля программиста». Моей первой мыслью было, что он / она имеет в виду вещи, которые находятся вне контроля программистов API . Но на самом деле проверенные исключения при правильном использовании должны действительно относиться к вещам, которые находятся вне контроля как программиста клиента, так и программиста API. Я думаю, что это ключ к тому, чтобы не злоупотреблять проверенными исключениями.

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

Итак, API делает это явным: будут случаи, когда этот файл не будет существовать в то время, когда вы мне звоните, и вам, черт побери, лучше с этим справиться.

Это было бы яснее с контр-кейсом. Представьте, что я пишу таблицу API. У меня есть модель таблицы где-то с API, включая этот метод:

public RowData getRowData(int row) 

Теперь, как программист API, я знаю, что будут случаи, когда некоторый клиент передает отрицательное значение для строки или значение строки вне таблицы. Поэтому у меня может возникнуть соблазн выдать проверенное исключение и заставить клиента с ним справиться:

public RowData getRowData(int row) throws CheckedInvalidRowNumberException

(Конечно, я бы не назвал это «Проверено».)

Это плохое использование проверенных исключений. Код клиента будет полон вызовов для извлечения данных строки, каждый из которых должен будет использовать try / catch, и для чего? Собираются ли они сообщить пользователю, что был найден неправильный ряд? Вероятно, нет - потому что, каким бы ни был пользовательский интерфейс, окружающий мое табличное представление, он не должен позволять пользователю переходить в состояние, когда запрашивается недопустимая строка. Так что это ошибка со стороны клиентского программиста.

Программист API все еще может предсказать, что клиент будет кодировать такие ошибки и должен обрабатывать их с исключением времени выполнения, например IllegalArgumentException.

С проверенным исключением getRowData, это явно тот случай, который приведет к тому, что ленивый программист Хейлсберга просто добавит пустые уловы. Когда это происходит, недопустимые значения строк не будут очевидны даже для отладчика тестировщика или разработчика клиента, скорее, они приведут к ошибкам, которые трудно точно определить источник. Ракеты Арианны взорвутся после запуска.

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

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

  1. Вне контроля клиента или Закрыто против Открытого:

    Проверенные исключения следует использовать только в том случае, если ошибка не контролируется как API, так и клиентским программистом. Это связано с тем, насколько открыта или закрыта система. В ограниченном пользовательском интерфейсе, где клиентский программист имеет контроль, скажем, над всеми кнопками, командами клавиатуры и т. Д., Которые добавляют и удаляют строки из табличного представления (закрытая система), это ошибка программирования клиента, если он пытается извлечь данные из несуществующий ряд В файловой операционной системе, где любое количество пользователей / приложений может добавлять и удалять файлы (открытая система), возможно, что файл, запрашиваемый клиентом, был удален без их ведома, поэтому следует ожидать, что он будет иметь с ним дело. ,

  2. Ubiquity:

    Проверенные исключения не должны использоваться в вызове API, который часто выполняется клиентом. Под часто я имею в виду много мест в клиентском коде - не часто во времени. Таким образом, клиентский код не склонен многократно открывать один и тот же файл, но мое табличное представление встречается RowDataвезде разными способами. В частности, я собираюсь написать много кода, как

    if (model.getRowData().getCell(0).isEmpty())

    и будет больно каждый раз оборачиваться в try / catch.

  3. Информирование пользователя:

    Проверенные исключения следует использовать в тех случаях, когда вы можете представить полезное сообщение об ошибке, которое будет представлено конечному пользователю. Это "а что ты будешь делать, когда это произойдет?" вопрос, который я поднял выше. Это также относится к пункту 1. Поскольку вы можете предсказать, что что-то за пределами вашей системы клиент-API может привести к отсутствию файла, вы можете разумно сообщить об этом пользователю:

    "Error: could not find the file 'goodluckfindingthisfile'"

    Так как ваш неправильный номер строки был вызван внутренней ошибкой и не по вине пользователя, на самом деле нет никакой полезной информации, которую вы можете им дать. Если ваше приложение не позволяет исключениям среды выполнения попадать на консоль, оно, вероятно, в конечном итоге выдаст им какое-то ужасное сообщение, например:

    "Internal error occured: IllegalArgumentException in ...."

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

Таковы мои правила. Несколько придумано, и, несомненно, будут исключения (пожалуйста, помогите мне уточнить их, если хотите). Но мой главный аргумент в том, что бывают случаи, FileNotFoundExceptionкогда проверяемое исключение является такой же важной и полезной частью контракта API, как и типы параметров. Поэтому мы не должны обходиться без него только потому, что оно используется не по назначению.

Извините, я не хотел делать это так долго и вафельно. Позвольте мне закончить с двумя предложениями:

A: Программисты API: экономно используйте проверенные исключения, чтобы сохранить их полезность. В случае сомнений используйте непроверенное исключение.

B: Клиентские программисты: привычка создавать упакованное исключение (Google google) на ранней стадии разработки. JDK 1.4 и более поздние версии предоставляют конструктор RuntimeExceptionдля этого, но вы также можете легко создать и свой собственный. Вот конструктор:

public RuntimeException(Throwable cause)

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

try {
  overzealousAPI(thisArgumentWontWork);
}
catch (OverzealousCheckedException exception) {
  throw new RuntimeException(exception);  
}

Поместите это в один из маленьких шаблонов кода вашей IDE и используйте его, когда вам лень. Таким образом, если вам действительно нужно обработать проверенное исключение, вы будете вынуждены вернуться и разобраться с ним, увидев проблему во время выполнения. Потому что, поверьте мне (и Андерсу Хейлсбергу), вы никогда не вернетесь к этому TODO в своем

catch (Exception e) { /* TODO deal with this at some point (yeah right) */}
274
10.03.2018 16:43:39
Открытие файлов на самом деле является хорошим примером для абсолютно контрпродуктивных проверенных исключений. Поскольку большую часть времени код, открывающий файл, НЕ МОЖЕТ сделать ничего полезного в отношении исключения - это лучше сделать несколькими слоями стека вызовов. Проверенное исключение вынуждает вас загромождать сигнатуры вашего метода просто для того, чтобы вы могли делать то, что сделали бы в любом случае - обрабатывать исключение в том месте, где это наиболее целесообразно.
Michael Borgwardt 5.05.2009 11:02:56
@Michael: Согласен, вы, как правило, можете обрабатывать эти исключения ввода-вывода на несколько уровней выше, но я не слышал, чтобы вы отрицали, что как клиент API вы должны ожидать этого. По этой причине я думаю, что проверенные исключения являются подходящими. Да, вам придется объявлять «throws» в каждом стеке вызовов метода up, но я не согласен с тем, что это беспорядок. Это полезная декларативная информация в подписи вашего метода. Вы говорите: мои низкоуровневые методы могут столкнуться с отсутствующим файлом, но они оставят вам обработку этого. Я не вижу ничего, чтобы терять, и только хороший, чистый код, чтобы получить
Rhubarb 11.05.2009 12:02:08
@Rhubarb: +1, очень интересный ответ, показывает аргументы для обеих сторон. Отлично. О вашем последнем комментарии: обратите внимание, что объявление бросков каждого метода вверх по стеку вызовов не всегда возможно, особенно если вы реализуете интерфейс на этом пути.
R. Martinho Fernandes 4.08.2009 18:34:52
Это очень сильный аргумент (и я согласен с ним в целом), но есть случай, когда он не работает: общие обратные вызовы. Если у меня есть какая-то библиотека, вызывающая какой-то определенный пользователем код, то я не знаю (как я знаю, в java), чтобы эта библиотека распространяла проверенные исключения из кода пользователя, который она вызывает, в код пользователя, который ее вызывает. Эта проблема также распространяется на другие части системы типов многих статически типизированных языков, которые не поддерживают надлежащие универсальные типы. Этот ответ будет улучшен путем упоминания об этом (и, возможно, ответ).
Mankarse 20.08.2011 08:13:34
Неправильно @Rhubarb. Проверенные исключения были предназначены для «непредвиденных обстоятельств», которые можно и нужно обрабатывать, но они всегда были несовместимы с наилучшей практикой «брось рано, поймай поздно». Открытие файла - пример, подобранный вишней, с которым можно справиться. Большинство IOException (например, запись байта), SQLException, RemoteException невозможно обработать осмысленным образом. Ошибка или повторная попытка должны быть на уровне «бизнес» или «запрос», а проверенные исключения - как они используются в библиотеках Java - являются ошибкой, которая делает это трудным. literatejava.com/exceptions/...
Thomas W 2.06.2014 02:01:16

Действительно, проверенные исключения, с одной стороны, повышают надежность и корректность вашей программы (вы вынуждены делать правильные объявления ваших интерфейсов - исключения, которые выдает метод, в основном являются специальным типом возврата). С другой стороны, вы сталкиваетесь с проблемой, заключающейся в том, что, поскольку исключения «всплывают», очень часто вам необходимо изменить целый ряд методов (всех вызывающих и вызывающих абонентов и т. Д.) При изменении одного исключения. метод бросков.

Проверенные исключения в Java не решают последнюю проблему; C # и VB.NET выбрасывают ребенка с водой.

Хороший подход, который идет по среднему пути, описан в этом документе OOPSLA 2005 (или связанном техническом отчете ).

Короче говоря, это позволяет вам сказать:, method g(x) throws like f(x)что означает, что g выбрасывает все исключения f бросков. Вуаля, проверил исключения без проблемы каскадных изменений.

Хотя это академическая статья, я бы посоветовал вам прочитать ее (частично), поскольку она хорошо объясняет преимущества и недостатки проверенных исключений.

6
10.10.2014 14:18:08

В статье « Эффективные исключения Java» подробно объясняется, когда использовать непроверенные и когда использовать отмеченные исключения. Вот некоторые цитаты из этой статьи, чтобы выделить основные моменты:

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

Ошибка: незапланированное условие, которое не позволяет методу достичь поставленной цели, которое не может быть описано без ссылки на внутреннюю реализацию метода.

(ТАК не позволяет таблицы, поэтому вы можете прочитать следующее с исходной страницы ...)

непредвиденные обстоятельства

  • Считается, что: часть дизайна
  • Ожидается, что произойдет: регулярно, но редко
  • Кого это волнует: вышестоящий код, который вызывает метод
  • Примеры: альтернативные режимы возврата
  • Best Mapping: проверенное исключение

Fault

  • Считается: неприятный сюрприз
  • Ожидается, что случится: никогда
  • Кого это волнует: людей, которым нужно решить проблему
  • Примеры: ошибки программирования, неисправности оборудования, ошибки конфигурации, отсутствующие файлы, недоступные серверы
  • Best Mapping: непроверенное исключение
20
14.11.2010 22:43:05
Я знаю, когда их использовать, я хочу знать, почему люди, которые не следуют этому совету ... не следуют этому совету :-)
TofuBeer 5.03.2009 15:45:38
Что такое ошибки программирования и как отличить их от ошибок использования ? Это ошибка программирования, если пользователь передает неверные аргументы программе? С точки зрения Java это может быть не программная ошибка, но с точки зрения сценария оболочки это программная ошибка. Так в чем же неверные аргументы args[]? Это непредвиденный случай или ошибка?
ceving 12.03.2013 14:50:03
@TofuBeer - Поскольку разработчики библиотеки Java, решили поставить все виды сбоев неустранимых низкого уровня , как проверяемые исключения , когда они явно должны были бесконтрольно . FileNotFound является единственным IOException, который должен быть проверен, например. Что касается JDBC - только соединение с базой данных, можно разумно считать непредвиденным обстоятельством . Все другие исключения SQLE должны были быть неудачными и непроверенными. Обработка ошибок должна быть корректной на уровне «бизнеса» или «запроса» - см. Рекомендации «Брось раньше, поймай поздно». Проверенные исключения являются препятствием для этого.
Thomas W 2.06.2014 02:21:43
В вашем аргументе есть один огромный недостаток. «Непредвиденные обстоятельства» НЕ должны обрабатываться посредством исключений, а должны возвращаться через бизнес-код и возвращаемые значения методов. Исключения составляют, как говорится в слове, ИСКЛЮЧИТЕЛЬНЫЕ ситуации, следовательно, недостатки.
Matteo Mosca 16.12.2014 16:15:48
@MatteoMosca Коды возврата ошибок, как правило, игнорируются, и этого достаточно, чтобы их дисквалифицировать. На самом деле, все необычное часто может быть обработано только где-то в стеке, и это пример использования исключений. Я мог бы представить что-то вроде File#openInputStreamвозвращения Either<InputStream, Problem>- если ты это имеешь в виду, тогда мы можем согласиться.
maaartinus 2.12.2017 15:12:18

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

Другая проблема с проверенными исключениями заключается в том, что они, как правило, используются не по назначению. Прекрасным примером этого является в java.sql.Connection«S close()методом. Он может выдать SQLException, даже если вы уже явно указали, что вы сделали с подключением. Какую информацию может закрыть (), возможно, передать, что вы заботитесь?

Обычно, когда я закрываю () соединение *, оно выглядит примерно так:

try {
    conn.close();
} catch (SQLException ex) {
    // Do nothing
}

Кроме того, не начинайте меня с различных методов синтаксического анализа и NumberFormatException .... TryParse .NET, который не вызывает исключений, намного проще в использовании, так что больно возвращаться к Java (мы используем как Java, так и C # где я работаю).

*В качестве дополнительного комментария, Connection.close () PooledConnection даже не закрывает соединение, но вам все равно придется перехватывать SQLException, потому что это проверенное исключение.

3
5.03.2009 17:22:19
Правильно, любой из драйверов может ... вопрос скорее "почему это должно волновать программиста?" как он все равно получил доступ к базе данных. Документы даже предупреждают вас, что вы всегда должны совершать () или откатывать () текущую транзакцию перед вызовом close ().
Powerlord 5.03.2009 17:52:52
Многие люди думают, что закрытие файла не может вызвать исключение ... stackoverflow.com/questions/588546/… Вы на 100% уверены, что нет случаев, когда это будет иметь значение?
TofuBeer 5.03.2009 21:50:09
Я на 100% уверен, что не было случаев, когда это имело бы значение, и что вызывающий не стал бы пытаться / ловить.
Martin 17.05.2009 20:06:56
Отличный пример с закрытием связей, Мартин! Я могу только перефразировать вас: если мы просто явно заявили, что мы закончили с подключением, зачем беспокоиться о том, что происходит, когда мы закрываем его? Есть еще такие случаи, когда программисту все равно, происходит ли исключение, и он абсолютно прав.
Piotr Sobczyk 29.10.2011 09:33:44
@PiotrSobczyk: Некоторые драйверы SQL будут кричать, если кто-то закрывает соединение после запуска транзакции, но не подтверждает и не откатывает его. ИМХО, кричать лучше, чем молчаливо игнорировать проблему, по крайней мере в тех случаях, когда крики не приводят к потере других исключений.
supercat 14.03.2014 00:21:58

SNR

Во-первых, проверенные исключения уменьшают «отношение сигнал / шум» для кода. Андерс Хейлсберг также говорит об императивном и декларативном программировании, которое является аналогичной концепцией. В любом случае рассмотрим следующие фрагменты кода:

Обновить пользовательский интерфейс из не-пользовательского потока в Java:

try {  
    // Run the update code on the Swing thread  
    SwingUtilities.invokeAndWait(() -> {  
        try {
            // Update UI value from the file system data  
            FileUtility f = new FileUtility();  
            uiComponent.setValue(f.readSomething());
        } catch (IOException e) {  
            throw new UncheckedIOException(e);
        }
    });
} catch (InterruptedException ex) {  
    throw new IllegalStateException("Interrupted updating UI", ex);  
} catch (InvocationTargetException ex) {
    throw new IllegalStateException("Invocation target exception updating UI", ex);
}

Обновление пользовательского интерфейса из не-пользовательского потока в C #:

private void UpdateValue()  
{  
   // Ensure the update happens on the UI thread  
   if (InvokeRequired)  
   {  
       Invoke(new MethodInvoker(UpdateValue));  
   }  
   else  
   {  
       // Update UI value from the file system data  
       FileUtility f = new FileUtility();  
       uiComponent.Value = f.ReadSomething();  
   }  
}  

Что кажется мне намного понятнее. Когда вы начинаете все больше и больше работать с пользовательским интерфейсом в Swing, проверенные исключения начинают становиться действительно раздражающими и бесполезными.

Побег из тюрьмы

Для реализации даже самых базовых реализаций, таких как интерфейс List в Java, проверены исключения как инструмент проектирования по контракту. Рассмотрим список, который поддерживается базой данных, файловой системой или любой другой реализацией, которая выдает проверенное исключение. Единственная возможная реализация - перехватить проверенное исключение и повторно выбросить его как исключение без проверки:

@Override
public void clear()  
{  
   try  
   {  
       backingImplementation.clear();  
   }  
   catch (CheckedBackingImplException ex)  
   {  
       throw new IllegalStateException("Error clearing underlying list.", ex);  
   }  
}  

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

Вывод

  • Ловля исключений отличается от их обработки.
  • Проверенные исключения добавляют шум к коду.
  • Обработка исключений хорошо работает в C # без них.

Я писал об этом ранее .

24
10.07.2017 18:15:17
В этом примере и Java, и C # просто позволяют распространяться исключениям, не обрабатывая их (Java через IllegalStateException). Разница в том, что вы можете обрабатывать исключение FileNotFoundException, но маловероятно, что обработка InvocationTargetException или InterruptedException будет полезной.
Luke Quinane 1.04.2009 04:25:09
А как в C # узнать, что может возникнуть исключение ввода-вывода? Также я бы никогда не бросил исключение из run ... Я считаю, что злоупотребление обработкой исключений. Извините, но для этой части вашего кода я просто пока вижу вашу сторону.
TofuBeer 1.04.2009 15:36:07
Мы добираемся туда :-) Итак, с каждым новым выпуском API вы должны прочесывать все ваши вызовы и искать какие-либо новые исключения, которые могут произойти? Это может легко случиться с внутренними API-интерфейсами компании, поскольку им не нужно беспокоиться о обратной совместимости.
TofuBeer 2.04.2009 16:38:50
Вы имели в виду уменьшение отношения сигнал / шум?
neo2862 15.11.2009 09:53:09
@ TofuBeer Не принуждают ли вы обновлять ваш код после того, как изменился интерфейс нижележащего API? Если бы у вас были только непроверенные исключения, вы бы получили сломанную / неполную программу, не зная об этом.
Francois Bourgeois 4.04.2013 10:52:20

Как уже говорили, проверенные исключения не существуют в байт-коде Java. Это просто механизм компиляции, мало похожий на другие проверки синтаксиса. Я вижу , проверяемые исключения много , как я вижу , что компилятор жалуется на избыточный условный: if(true) { a; } b;. Это полезно, но я мог сделать это специально, поэтому позвольте мне проигнорировать ваши предупреждения.

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

Исправьте плохие программы там! Не пытайтесь исправить язык, чтобы не допустить их! Для большинства людей «делать что-то с исключением» на самом деле просто говорит пользователю об этом. Я также могу рассказать пользователю о неконтролируемом исключении, так что держите проверенные классы исключений в моем API.

4
26.01.2018 15:51:16
Да, я просто хотел подчеркнуть разницу между недостижимым кодом (который генерирует ошибку) и условными выражениями с предсказуемым результатом. Я удалю этот комментарий позже.
Holger 29.01.2018 08:14:43

Я много читал об обработке исключений, даже если (большую часть времени) я не могу сказать, что я доволен или расстроен существованием проверенных исключений, это мое мнение: проверенные исключения в низкоуровневом коде (IO, работа в сети) ОС и т. Д.) И непроверенные исключения в высокоуровневых API / уровне приложений.

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

Проект, над которым я работаю, берет много библиотек и интегрирует их в один API, API, который полностью основан на непроверенных исключениях. Эти платформы предоставляют высокоуровневый API, который в начале был полон проверенных исключений и имел только несколько непроверенных исключения (Initialization Exception, ConfigurationException и т. д.) и, надо сказать, был не очень дружелюбным . Большую часть времени вам приходилось ловить или перебрасывать исключения, которые вы не знаете, как обрабатывать, или вас это даже не волнует (не путать с вами следует игнорировать исключения), особенно на стороне клиента, где один клик может вызвать 10 возможных (проверенных) исключений.

Текущая версия (3-я версия) использует только непроверенные исключения и имеет глобальный обработчик исключений, который отвечает за обработку всего необработанного. API предоставляет способ регистрации обработчиков исключений, который решает, будет ли исключение считаться ошибкой (в большинстве случаев это так), что означает запись и уведомление кого-либо, или это может означать что-то другое - например, это исключение AbortException, что означает прерывание текущего потока выполнения и не регистрировать ошибки, потому что это нежелательно. Конечно, чтобы отработать все пользовательские потоки, необходимо обработать метод run () с помощью try {...} catch (all).

public void run () {

try {
     ... do something ...
} catch (Throwable throwable) {
     ApplicationContext.getExceptionService().handleException("Handle this exception", throwable);
}

}

В этом нет необходимости, если вы используете WorkerService для планирования заданий (Runnable, Callable, Worker), который обрабатывает все за вас.

Конечно, это только мое мнение, и оно может быть неправильным, но это выглядит как хороший подход ко мне. После выхода проекта я увижу, будет ли то, что я считаю, хорошим для меня, хорошим и для других ... :)

0
18.05.2009 03:36:06

Хорошо ... Проверенные исключения не идеальны и имеют некоторые оговорки, но они действительно служат цели. При создании API существуют конкретные случаи сбоев, которые являются договорными для этого API. Если в контексте языка со строгой статической типизацией, такого как Java, если не используются проверенные исключения, то следует полагаться на специальную документацию и соглашения для передачи возможности ошибки. Это исключает все преимущества, которые компилятор может принести в обработке ошибок, и вы полностью оставлены на волю программистов.

Итак, один удаляет проверенное исключение, такое как было сделано в C #, как тогда можно программно и структурно передать возможность ошибки? Как сообщить клиентскому коду, что такие и такие ошибки могут возникать и что с ними нужно бороться?

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

Возьмем случай, когда исключение было сгенерировано где-то в нижней части уровней API и просто всплыло, потому что никто не знал, что эта ошибка может даже произойти, хотя это был тип ошибки, который был очень вероятен при вызове кода. бросил его (FileNotFoundException, например, в отличие от VogonsTrashingEarthExcept ... в этом случае не имеет значения, будем ли мы обрабатывать его или нет, так как с ним ничего не останется).

Многие утверждают, что неспособность загрузить файл была почти всегда концом света для процесса, и он должен умереть ужасной и мучительной смертью. Так что да ... конечно ... хорошо ... вы создаете API для чего-то, и он загружает файл в какой-то момент ... Я, как пользователь указанного API, могу только отвечать ... "Кто, черт возьми, вы решаете, когда мой программа должна вылететь! Конечно, учитывая выбор, где исключения поглощаются и не оставляют следов, или исключение EletroFlabbingChunkFluxManifoldChuggingException с трассировкой стека глубже, чем траншея Марианны, я бы взял последнее без малейших колебаний, но означает ли это, что это желательный способ справиться с исключением ? Разве мы не можем быть где-то посередине, где исключение будет переделываться и переноситься каждый раз, когда оно переходит на новый уровень абстракции, чтобы оно действительно что-то значило?

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

Если бы мы исключили проверенное исключение, мы могли бы также исключить возвращаемый тип для функций и всегда возвращать переменную «любого типа» ... Это сделало бы жизнь намного проще, не так ли?

6
7.08.2009 17:34:53
Проверенные исключения были бы полезны, если бы существовало декларативное средство сказать, что ни один из вызовов методов внутри блока не должен генерировать некоторые (или любые) проверенные исключения, и любые такие исключения должны автоматически переноситься и перебрасываться. Они могут быть еще более полезными, если вызовы методов, которые были объявлены как выбрасывающие проверенные исключения, обмениваются скоростью вызова / возвратом для скорости обработки исключений (так, чтобы ожидаемые исключения могли обрабатываться почти так же быстро, как и обычный поток программы). Однако в настоящее время ни одна из этих ситуаций не применима.
supercat 14.03.2014 00:20:04

Моя рецензия на c2.com все еще в основном не изменилась по сравнению с первоначальной формой: CheckedExceptionsAreIncompatibleWithVisitorPattern

В итоге:

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

Основное предположение CheckedExceptions - все объявленные исключения могут быть выброшены из любой точки, которая вызывает метод с этим объявлением. VisitorPattern показывает, что это предположение является ошибочным.

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

Что касается основной проблемы:

Моя общая идея - обработчик верхнего уровня должен интерпретировать исключение и отобразить соответствующее сообщение об ошибке. Я почти всегда вижу либо исключения ввода-вывода, исключения связи (по какой-то причине API-интерфейсы различаются), либо фатальные ошибки задачи (программные ошибки или серьезную проблему на сервере поддержки), поэтому это не должно быть слишком сложно, если мы разрешаем трассировку стека для серьезной проблема с сервером.

4
8.11.2009 16:47:50
Вы должны иметь что-то вроде DAGNodeException в интерфейсе, затем перехватить IOException и преобразовать его в DAGNodeException: public void call (DAGNode arg) throws DAGNodeException;
TofuBeer 7.11.2009 20:46:43
@TofuBeer, это точно моя точка зрения. Я считаю, что постоянное обертывание и развертывание исключений хуже, чем удаление отмеченных исключений.
Joshua 7.11.2009 22:48:43
Ну, тогда мы полностью не согласны ... но ваша статья по-прежнему не дает ответа на реальный вопрос о том, как вы не позволяете приложению отображать трассировку стека для пользователя при возникновении исключения во время выполнения.
TofuBeer 8.11.2009 01:54:43
@TofuBeer - когда это терпит неудачу, говорить пользователю, что он потерпел неудачу, правильно! Какая у вас альтернатива, кроме как «замаскировать» неудачу с «нулевыми» или неполными / неверными данными? Притворяться, что это удалось - ложь, которая только ухудшает положение. Имея 25-летний опыт работы в высоконадежных системах, логика повторения должна использоваться только осторожно и там, где это необходимо. Я также ожидал бы, что посетитель, скорее всего, снова потерпит неудачу, независимо от того, сколько раз вы пытаетесь это повторить. Если вы не управляете самолетом, переключение на вторую версию того же алгоритма нецелесообразно и неправдоподобно (и может в любом случае потерпеть неудачу).
Thomas W 2.06.2014 03:01:15

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

Единственный сценарий, где эта функция может раздражать, - это создание прототипов. Эту проблему можно решить, добавив некоторый механизм в язык (например, некоторую аннотацию @supresscheckedexceptions). Но для обычного программирования я думаю, что проверенные исключения - это хорошая вещь.

0
2.06.2014 15:06:38
Лучшая практика «бросай рано, лови поздно» несовместима с настойчивостью проверяемого исключения, что они должны быть обработаны немедленно . Это также предотвращает подходы функционального программирования FP. Смотрите: literatejava.com/exceptions/…
Thomas W 2.06.2014 03:02:25
Экспоненциальное расширение через дерево вызовов фактически является требованием немедленной обработки. Это могло бы быть целесообразным, если бы оно применялось только к непредвиденным обстоятельствам , которые являются предсказуемыми и потенциально восстанавливаемыми, но «проверенное» поведение было ошибочно расширено до широкого диапазона непредсказуемых и неисправимых сбоев . «Открыть файл» или «подключить JDBC» разумно требовать проверки - большинство других IOException, SQLException, RemoteException не так. Это было серьезной ошибкой в ​​дизайне библиотеки Java. Смотрите мой ответ и базовый учебник по обработке исключений.
Thomas W 2.06.2014 21:59:32
«Опоздание на задержку» основано на уровне, на котором может быть изолирован сбой - в большинстве случаев это уровень бизнес / запроса или исходящего соединения / запроса. Просто и правильно.
Thomas W 4.06.2014 02:23:26
Возврат null / false / -1 является неправильной практикой, поскольку он неправильно представляет успех вашего клиента! Это строгое «нет-нет», поскольку оно позволяет продолжить выполнение с неполными / недействительными / неверными данными, чтобы позже потерпеть неудачу (плохо) или зафиксировать в БД (хуже). Если части бизнес-логики действительно являются необязательными , что вы не указали, то попытка / отловка позволяют им продолжить с предупреждением. Недопустимые значения и распространение неверных данных по приложению не являются ни хорошими, ни необходимыми.
Thomas W 4.06.2014 02:32:45
Обработка исключений Лучшей практики основана на самом деле , как исключения / ошибки могут быть наилучшим образом правильно обрабатываются (лесозаготовительная, отчетность, иногда выздоровел). Это наука, а не искусство. Добиться 100% оптимальности и правильности на самом деле просто и легко - до тех пор, пока нас не подтолкнет (из-за ошибочного дизайна библиотеки) «справиться рано». Как мы видим, это в основном только поощряет ошибочную практику.
Thomas W 4.06.2014 02:36:43

Проблема

Худшая проблема, которую я вижу с механизмом обработки исключений, заключается в том, что он вводит дублирование кода в большом масштабе ! Давайте будем честными: в большинстве проектов в 95% случаев все, что действительно нужно делать разработчикам, - это как-то сообщить об этом пользователю (а в некоторых случаях и команде разработчиков, например, отправив электронное письмо). почта с трассировкой стека). Таким образом, обычно одна и та же строка / блок кода используется в каждом месте, где обрабатывается исключение.

Давайте предположим, что мы делаем простую регистрацию в каждом блоке catch для некоторого типа проверяемого исключения:

try{
   methodDeclaringCheckedException();
}catch(CheckedException e){
   logger.error(e);
}

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

Подождите минутку ... мы действительно собираемся отредактировать все эти несколько сотен мест в коде ?! Вы поняли мою точку зрения :-).

Решение

Чтобы решить эту проблему, мы представили концепцию обработчиков исключений (которые я далее буду называть EH) для централизации обработки исключений. Каждому классу, который должен обрабатывать исключения, экземпляр обработчика исключений внедряется нашей средой внедрения зависимостей . Таким образом, типичная схема обработки исключений теперь выглядит так:

try{
    methodDeclaringCheckedException();
}catch(CheckedException e){
    exceptionHandler.handleError(e);
}

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

Конечно, для более сложных случаев мы можем реализовать несколько подклассов EH и использовать функции, которые предоставляет нам наша структура DI. Изменяя нашу конфигурацию DI-инфраструктуры, мы можем легко переключать реализацию EH глобально или предоставлять конкретные реализации EH для классов с особыми потребностями обработки исключений (например, с помощью аннотации Guice @Named).

Таким образом, мы можем дифференцировать поведение обработки исключений в разработке и выпуске версии приложения (например, разработка - регистрация ошибки и остановка приложения, продолжение - регистрация ошибки с более подробной информацией и разрешение приложению продолжить выполнение) без каких-либо усилий.

Последняя вещь

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

5
19.07.2016 07:59:43
Исключения придуманы, чтобы сделать что-то полезное с ними. Запись их в файл журнала или рендеринг красивого окна бесполезна, потому что первоначальная проблема не решается этим. Чтобы сделать что-то полезное, нужно попробовать другую стратегию решения. Примеры: Если я не могу получить свои данные от сервера AI, попробуйте их на сервере B. Или, если алгоритм A вызывает переполнение кучи, я пробую алгоритм B, который работает намного медленнее, но может быть успешным.
ceving 12.03.2013 15:12:10
@ceving Да, в теории все хорошо и верно. Но теперь давайте вернемся к практике слова. Пожалуйста, ответьте действительно честно, как часто вы делаете это в своем проекте реального слова? Какая часть catchблоков в этом реальном проекте делает что-то действительно «полезное» с исключением? 10% было бы хорошо. Обычные проблемы, которые генерируют исключения, похожи на попытку считывания конфигурации из несуществующего файла, OutOfMemoryErrors, NullPointerExceptions, ошибки целостности ограничений базы данных и т. Д., И т. Д. Вы действительно пытаетесь корректно восстановить все из них? Я тебе не верю :). Часто просто нет возможности выздороветь.
Piotr Sobczyk 13.03.2013 18:51:41
@PiotrSobczyk: Если программа выполняет какое-либо действие в результате запроса suer, и операция каким-то образом завершается с ошибкой, которая ничего не повредила в состоянии системы, уведомление пользователя о невозможности завершения операции является весьма полезным способ справиться с ситуацией. Самым большим недостатком исключений в C # и .net является то, что нет единого способа установить, не было ли что-либо в состоянии системы повреждено.
supercat 15.03.2013 19:58:28
Правильно, @PiotrSobczyk. Чаще всего единственное правильное действие, предпринимаемое в ответ на исключение, - это откат транзакции и возврат ответа об ошибке. Идеи «решения исключений» подразумевают знание и авторитет, которого у нас нет (и не должно быть), и нарушают инкапсуляцию. Если наше приложение не является БД, мы не должны пытаться исправить БД. Чистый сбой и отказ от записи ошибочных данных, ceving, достаточно полезен .
Thomas W 2.06.2014 02:50:44
@PiotrSobczyk Вчера я имел дело с исключением «не удалось прочитать объект» (которое возникнет только потому, что базовая база данных была обновлена ​​до программного обеспечения - что никогда не должно происходить, но это возможно из-за человеческой ошибки) при переходе на Историческая версия базы данных гарантированно указывает на старую версию объекта.
Eponymous 10.10.2014 13:23:01

Эта статья - лучший текст по обработке исключений в Java, который я когда-либо читал.

Он одобряет непроверенные над проверенными исключениями, но этот выбор объясняется очень подробно и основан на веских аргументах.

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

Возьмем случай, когда исключение было сгенерировано где-то в нижней части уровней API и просто всплыло, потому что никто не знал, что эта ошибка может даже произойти, хотя это был тип ошибки, который был очень вероятен при вызове кода. бросил его (FileNotFoundException, например, в отличие от VogonsTrashingEarthExcept ... в этом случае не имеет значения, будем ли мы обрабатывать его или нет, так как с ним ничего не останется).

Автор "Отзывы":

Абсолютно неверно предполагать, что все исключения во время выполнения не должны быть перехвачены и допущены к распространению на самую верхушку приложения. (...) Для каждого исключительного условия, которое должно быть четко обработано - системными / бизнес-требованиями - программисты должны решить, где его перехватить и что делать, когда условие перехватывается. Это должно быть сделано строго в соответствии с реальными потребностями приложения, а не на основе предупреждения компилятора. Все остальные ошибки должны свободно распространяться на самый верхний обработчик, где они будут регистрироваться, и будет предпринято изящное (возможно, завершение) действие.

И главная мысль или статья:

Когда дело доходит до обработки ошибок в программном обеспечении, единственное безопасное и правильное предположение, которое может когда-либо быть сделано, состоит в том, что сбой может произойти абсолютно в каждой подпрограмме или модуле, который существует!

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

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

4
13.03.2013 18:58:13

Чтобы попытаться ответить только на оставшийся без ответа вопрос:

Если вы бросаете подклассы RuntimeException вместо подклассов Exception, то как вы узнаете, что вы должны ловить?

Вопрос содержит ложные рассуждения ИМХО. Тот факт, что API сообщает вам, что он выдает, не означает, что вы будете работать с ним одинаково во всех случаях. Иными словами, исключения, которые нужно отлавливать, различаются в зависимости от контекста, в котором вы используете компонент, вызывающий исключение.

Например:

Если я пишу тестер соединений для базы данных или что-то для проверки правильности ввода пользователем XPath, то я, вероятно, захочу отловить и сообщить обо всех проверенных и непроверенных исключениях, которые выдает операция.

Однако, если я пишу обработчик, я, скорее всего, буду трактовать исключение XPathException (проверено) так же, как NPE: я бы позволил ему запускаться до вершины рабочего потока, пропустил оставшуюся часть этого пакета, проблема (или отправить его в службу поддержки для диагностики) и оставить отзыв для пользователя, чтобы связаться со службой поддержки.

5
5.07.2012 10:44:14
Точно. Легкий и понятный способ обработки исключений. Как говорит Дейв, правильная обработка исключений обычно выполняется на высоком уровне . «Брось рано, поймай поздно» - это принцип. Проверенные исключения затрудняют это.
Thomas W 2.06.2014 03:05:10

Мы видели некоторые ссылки на главного архитектора C #.

Вот альтернативная точка зрения парня из Java о том, когда использовать проверенные исключения. Он признает многие негативы, о которых упоминали другие: Эффективные исключения

1
2.08.2012 16:54:02
Проблема с проверенными исключениями в Java проистекает из более глубокой проблемы, заключающейся в том, что слишком много информации инкапсулируется в ТИПе исключения, а не в свойствах экземпляра. Было бы полезно иметь проверенные исключения, если «проверено» было атрибутом сайтов броска / перехвата, и если можно было декларативно указать, должно ли проверенное исключение, которое выходит из блока кода, оставаться проверенным исключением или быть замеченным любым включающим блоком в качестве непроверенного исключения; аналогично, блоки catch должны иметь возможность указывать, что им нужны только проверенные исключения.
supercat 2.08.2012 18:33:52
Предположим, что подпрограмма поиска по словарю определена для того, чтобы генерировать исключение определенного типа, если была предпринята попытка получить доступ к несуществующему ключу. Для клиентского кода может быть разумным поймать такое исключение. Однако, если какой-то метод, используемый процедурой поиска, генерирует исключение того же типа таким образом, которого процедура поиска не ожидает, клиентский код, вероятно, не должен его перехватывать. Если флажок check-ness будет свойством экземпляров исключений , сайтов-бросков и сайтов-ловушек, это позволит избежать таких проблем. Клиент будет ловить «проверенные» исключения этого типа, избегая неожиданных.
supercat 2.08.2012 18:38:03

Хорошим доказательством того, что Checked Exception не нужны, являются:

  1. Много фреймворка, который делает некоторую работу для Java. Как Spring, который оборачивает исключение JDBC в непроверенные исключения, выбрасывая сообщения в журнал
  2. Много языков, которые пришли после Java, даже сверху на платформе Java - они не используют их
  3. Проверенные исключения, это добрый прогноз о том, как клиент будет использовать код, который выдает исключение. Но разработчик, который пишет этот код, никогда не узнает о системе и бизнесе, в котором работает клиент кода. Например, методы Interfcace, которые вынуждают генерировать проверенное исключение. В системе существует 100 реализаций, 50 или даже 90 реализаций не выдают это исключение, но клиент все равно должен перехватить это исключение, если он ссылается на этот интерфейс. Эти 50 или 90 реализаций имеют тенденцию обрабатывать эти исключения внутри себя, помещая исключения в журнал (и это хорошее поведение для них). Что нам с этим делать? Мне бы лучше иметь некоторую базовую логику, которая бы выполняла всю эту работу - отправка сообщения в журнал. И если я, как клиент кода, чувствую, что мне нужно обработать исключение - я сделаю это.
  4. Другой пример, когда я работаю с I / O в Java, это заставляет меня проверять все исключения, если файл не существует? что мне с этим делать? Если он не существует, система не перейдет к следующему шагу. Клиент этого метода не получит ожидаемое содержимое из этого файла - он может обработать исключение времени выполнения, в противном случае я должен сначала проверить Checked Exception, поместить сообщение в журнал, а затем выбросить исключение из метода. Нет ... нет - я бы лучше сделал это автоматически с RuntimeEception, который делает это / загорается автоматически. Нет никакого смысла обрабатывать это вручную - я был бы рад, что увидел сообщение об ошибке в журнале (AOP может помочь с этим .. что-то, что исправляет Java). Если, в конце концов, я угадаю, что система должна показывать всплывающее сообщение конечному пользователю - я покажу это, не проблема.

Я был счастлив, если бы java предоставил мне выбор, что использовать при работе с основными библиотеками, такими как I / O. Like предоставляет две копии одних и тех же классов - один обернут в RuntimeEception. Тогда мы можем сравнить, что люди будут использовать . На данный момент, тем не менее, многим лучше пойти на какую-нибудь платформу на Java или другой язык. Как Скала, JRuby, что угодно. Многие просто верят, что SUN был прав.

2
13.03.2014 21:25:49
Вместо того, чтобы иметь две версии классов, должен быть краткий способ указать, что ни один из вызовов методов, сделанных блоком кода, не должен генерировать исключения определенных типов, и что любые такие исключения должны быть обернуты с помощью некоторых указанных средств и rethrown (по умолчанию создайте новый RuntimeExceptionс соответствующим внутренним исключением). К сожалению, более лаконично иметь внешний метод throwsкак исключение из внутреннего метода, чем включать в него исключения из внутреннего метода, когда последний вариант действий чаще будет правильным.
supercat 13.05.2013 19:03:45

Категории исключений

Когда я говорю об исключениях, я всегда ссылаюсь на статью в блоге Эрика Липперта об исключениях Vexing . Он помещает исключения в следующие категории:

  • Неустранимый - эти исключения не ваша вина : вы не можете предотвратить, и вы не можете разумно справиться с ними. Например, OutOfMemoryErrorили ThreadAbortException.
  • Boneheaded - эти исключения являются вашей ошибкой : вы должны были предотвратить их, и они представляют собой ошибки в вашем коде. Например ArrayIndexOutOfBoundsException, NullPointerExceptionили любой IllegalArgumentException.
  • Vexing - эти исключения не являются исключительными , не ваша вина, вы не можете предотвратить их, но вам придется иметь дело с ними. Они часто являются результатом неудачного проектного решения, такие , как бросание NumberFormatExceptionиз Integer.parseIntвместо того , обеспечивая Integer.tryParseIntметод , который возвращает логическое значение FALSE на синтаксического анализа отказа.
  • Экзогенные - Эти исключения, как правило, являются исключительными , не по вашей вине, вы не можете (разумно) предотвратить их, но вы должны справиться с ними . Например, FileNotFoundException.

Пользователь API:

  • не должен обрабатывать фатальные или безголовые исключения.
  • должен обрабатывать неприятные исключения, но они не должны встречаться в идеальном API.
  • должны обрабатывать экзогенные исключения.

Проверенные исключения

Тот факт, что пользователь API должен обработать конкретное исключение, является частью контракта метода между вызывающим и вызываемым. В контракте, среди прочего, указывается количество и типы аргументов, которые ожидает вызываемый объект, тип возвращаемого значения, которого может ожидать вызывающий объект, и исключения, которые ожидается от вызывающего объекта .

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

Проверенное исключение - это исключение, которое должно быть обработано . Обрабатывать исключение можно так же просто, как проглотить его. Там! Исключение обработано. Период. Если разработчик хочет справиться с этим, хорошо. Но он не может игнорировать исключение и был предупрежден.

Проблемы API

Но любой API, который проверил неприятные и фатальные исключения (например, JCL), создаст ненужную нагрузку на пользователей API. Такие исключения должны быть обработаны, но либо исключение настолько распространено, что оно не должно быть исключением, либо ничего не может быть сделано при его обработке. И это заставляет разработчиков Java ненавидеть проверенные исключения.

Кроме того, многие API не имеют надлежащей иерархии классов исключений, в результате чего все виды неэкзогенных причин исключений представляются одним проверенным классом исключений (например IOException). И это также заставляет разработчиков Java ненавидеть проверенные исключения.

Вывод

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

Так что не ненавидьте Java и его проверенные исключения. Вместо этого ненавидите API, которые злоупотребляют проверенными исключениями.

13
12.05.2013 21:00:36
И злоупотреблять ими, не имея иерархии.
TofuBeer 12.05.2013 17:02:31
FileNotFound и установление соединения JDBC / сети являются непредвиденными и корректными для проверки исключениями, поскольку они являются предсказуемыми и (возможно) восстанавливаемыми. Большинство других IOExceptions, SQLExceptions, RemoteException и т. Д. Являются непредсказуемыми и неисправимыми сбоями и должны были быть исключениями времени выполнения. Из-за ошибочного дизайна библиотеки Java, мы все были в замешательстве с этой ошибкой и теперь в основном используем Spring & Hibernate (которые правильно разработали свой дизайн).
Thomas W 2.06.2014 02:45:18
Вы обычно должны обрабатывать исключения с головой, хотя вы можете не захотеть называть это «обработкой». Например, на веб-сервере я регистрирую их и показываю пользователю 500. Поскольку исключение является неожиданным, есть все, что я могу сделать до исправления ошибки.
maaartinus 2.12.2017 15:19:08

На мой взгляд, проверенные исключения - очень хорошая концепция. К сожалению, у большинства программистов, которые работали вместе, у меня есть другое мнение, так что в проектах часто используется неправильная обработка исключений. Я видел, что большинство программистов создают один (только один) класс исключений, подкласс RuntimeException. Это содержит сообщение, иногда многоязычный ключ. У меня нет шансов спорить против этого. У меня такое впечатление, что я разговариваю со стеной, когда объясняю им, что такое анти-паттерны, что такое контракты методов ... Я немного разочарован.

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

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

-2
22.08.2013 22:11:25

Я знаю, что это старый вопрос, но я боролся с проверенными исключениями, и мне есть что добавить. Пожалуйста, прости меня за это!

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

Возьмите хороший старый Listинтерфейс Java . У нас есть общие реализации в памяти, такие как ArrayListи LinkedList. У нас также есть класс скелета, AbstractListкоторый позволяет легко создавать новые типы списков. Для списка только для чтения нам нужно реализовать только два метода: size()и get(int index).

Этот пример WidgetListкласса читает некоторые объекты фиксированного размера типа Widget(не показаны) из файла:

class WidgetList extends AbstractList<Widget> {
    private static final int SIZE_OF_WIDGET = 100;
    private final RandomAccessFile file;

    public WidgetList(RandomAccessFile file) {
        this.file = file;
    }

    @Override
    public int size() {
        return (int)(file.length() / SIZE_OF_WIDGET);
    }

    @Override
    public Widget get(int index) {
        file.seek((long)index * SIZE_OF_WIDGET);
        byte[] data = new byte[SIZE_OF_WIDGET];
        file.read(data);
        return new Widget(data);
    }
}

Предоставляя виджеты с помощью знакомого Listинтерфейса, вы можете получить элементы ( list.get(123)) или выполнить итерацию списка ( for (Widget w : list) ...) без необходимости знать о WidgetListсебе. Можно передать этот список любым стандартным методам, которые используют общие списки, или обернуть его в Collections.synchronizedList. Код, использующий его, не должен знать и заботиться о том, создаются ли «виджеты» на месте, поступают ли они из массива, читаются ли они из файла, базы данных, или из всей сети, или из будущего ретранслятора подпространства. Он все равно будет работать правильно, потому что Listинтерфейс реализован правильно.

За исключением того, что это не так. Вышеупомянутый класс не компилируется, потому что методы доступа к файлу могут выдавать IOExceptionпроверенное исключение, которое вы должны «поймать или указать». Вы не можете указать его как выброшенный - компилятор не позволит вам, потому что это нарушит контракт Listинтерфейса. И нет никакого полезного способа, которым WidgetListсам мог бы обработать исключение (как я объясню позже).

Очевидно, единственное, что нужно сделать, это перехватить и перебросить проверенные исключения как неконтролируемое исключение:

@Override
public int size() {
    try {
        return (int)(file.length() / SIZE_OF_WIDGET);
    } catch (IOException e) {
        throw new WidgetListException(e);
    }
}

public static class WidgetListException extends RuntimeException {
    public WidgetListException(Throwable cause) {
        super(cause);
    }
}

((Редактировать: Java 8 добавила UncheckedIOExceptionкласс именно для этого случая: для перехвата и перебрасывания IOExceptions через границы полиморфных методов. Это доказывает мою точку зрения!))

Так что проверенные исключения просто не работают в подобных случаях. Вы не можете бросить их. То же самое для умного, Mapподдерживаемого базой данных, или реализации, java.util.Randomсвязанной с источником квантовой энтропии через COM-порт. Как только вы пытаетесь сделать что-то новое с использованием полиморфного интерфейса, концепция проверенных исключений не срабатывает. Но проверенные исключения настолько коварны, что они все равно не оставят вас в покое, потому что вам все равно придется перехватывать и отбрасывать любые из методов более низкого уровня, загромождая код и загромождая трассировку стека.

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

На самом деле, вы можете бросить необъявленные проверенные исключения, если вы прибегаете к взлому. Во время выполнения JVM не заботится о проверенных правилах исключений, поэтому нам нужно обмануть только компилятор. Самый простой способ сделать это - использовать дженерики. Это мой метод для него (имя класса показано, потому что (до Java 8) это требуется в синтаксисе вызова для универсального метода):

class Util {
    /**
     * Throws any {@link Throwable} without needing to declare it in the
     * method's {@code throws} clause.
     * 
     * <p>When calling, it is suggested to prepend this method by the
     * {@code throw} keyword. This tells the compiler about the control flow,
     * about reachable and unreachable code. (For example, you don't need to
     * specify a method return value when throwing an exception.) To support
     * this, this method has a return type of {@link RuntimeException},
     * although it never returns anything.
     * 
     * @param t the {@code Throwable} to throw
     * @return nothing; this method never returns normally
     * @throws Throwable that was provided to the method
     * @throws NullPointerException if {@code t} is {@code null}
     */
    public static RuntimeException sneakyThrow(Throwable t) {
        return Util.<RuntimeException>sneakyThrow1(t);
    }

    @SuppressWarnings("unchecked")
    private static <T extends Throwable> RuntimeException sneakyThrow1(
            Throwable t) throws T {
        throw (T)t;
    }
}

Ура! Используя это, мы можем выбросить проверенное исключение на любую глубину стека, не объявляя его, не оборачивая его RuntimeExceptionи не загромождая трассировку стека! Снова используем пример «WidgetList»:

@Override
public int size() {
    try {
        return (int)(file.length() / SIZE_OF_WIDGET);
    } catch (IOException e) {
        throw sneakyThrow(e);
    }
}

К сожалению, последнее оскорбление проверенных исключений состоит в том, что компилятор отказывается позволить вам перехватить проверенное исключение, если, по его ошибочному мнению, оно не могло быть сгенерировано. (Непроверенные исключения не имеют этого правила.) Чтобы поймать скрытно выброшенное исключение, мы должны сделать это:

try {
    ...
} catch (Throwable t) { // catch everything
    if (t instanceof IOException) {
        // handle it
        ...
    } else {
        // didn't want to catch this one; let it go
        throw t;
    }
}

Это немного неудобно, но, с другой стороны, он все же немного проще, чем код для извлечения проверенного исключения, которое было заключено в a RuntimeException.

К счастью, здесь throw t;утверждение является допустимым, даже несмотря на то, что tпроверяется тип , благодаря правилу, добавленному в Java 7 о повторном отбрасывании перехваченных исключений.


Когда проверяемые исключения встречаются с полиморфизмом, возникает и обратный случай: когда метод специфицируется как потенциально вызывающий исключение, а переопределенная реализация - нет. Например, абстрактный класс OutputStream«S writeметоды все указать throws IOException. ByteArrayOutputStreamявляется подклассом, который пишет в массив в памяти вместо истинного источника ввода / вывода. Его переопределенные writeметоды не могут вызывать IOExceptions, поэтому у них нет throwsпредложения, и вы можете вызывать их, не беспокоясь о требовании «поймать или указать».

Кроме не всегда. Предположим, что Widgetесть метод для сохранения его в поток:

public void writeTo(OutputStream out) throws IOException;

Объявление этого метода для принятия простого OutputStream- правильное решение, поэтому его можно использовать полиморфно со всеми видами выходных данных: файлами, базами данных, сетью и т. Д. И массивы в памяти. Однако с массивом в памяти существует ложное требование для обработки исключения, которое на самом деле не может произойти:

ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
    someWidget.writeTo(out);
} catch (IOException e) {
    // can't happen (although we shouldn't ignore it if it does)
    throw new RuntimeException(e);
}

Как обычно, мешают проверенные исключения. Если ваши переменные объявлены как базовый тип, который имеет более открытые требования к исключениям, вы должны добавить обработчики для этих исключений, даже если вы знаете, что они не возникнут в вашем приложении.

Но подождите, проверенные исключения на самом деле настолько раздражают, что даже не позволят вам сделать обратное! Представьте, что вы в настоящий момент перехватываете любые IOExceptionброски, вызванные writeвызовами an OutputStream, но вы хотите изменить объявленный тип переменной на a ByteArrayOutputStream, компилятор будет ругать вас за попытку отловить проверенное исключение, которое, по его словам, не может быть выдано.

Это правило вызывает некоторые абсурдные проблемы. Например, один из трех writeметодов OutputStreamявляется не переопределены ByteArrayOutputStream. В частности, write(byte[] data)это удобный метод, который записывает полный массив путем вызова write(byte[] data, int offset, int length)со смещением 0 и длиной массива. ByteArrayOutputStreamпереопределяет метод с тремя аргументами, но наследует метод удобства с одним аргументом как есть. Унаследованный метод делает абсолютно правильные вещи, но он включает нежелательное throwsпредложение. Возможно, это было упущением при разработке ByteArrayOutputStream, но они никогда не смогут это исправить, потому что это нарушит совместимость исходного кода с любым кодом, который перехватывает исключение - исключение, которое никогда не было, никогда не будет выброшено!

Это правило раздражает и во время редактирования и отладки. Например, иногда я временно закомментирую вызов метода, и если бы он мог вызвать проверенное исключение, компилятор теперь будет жаловаться на существование локальных tryи catchблоков. Поэтому я тоже должен это закомментировать, и теперь при редактировании кода внутри IDE отступ будет на неправильный уровень, потому что {и }закомментированы. Г! Это небольшая жалоба, но кажется, что единственное, что проверенные исключения когда-либо делают, это вызывает проблемы.


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

Вместо этого мы получаем следующую идиому, которая широко используется как способ закрыть компилятор:

try {
    ...
} catch (SomeStupidExceptionOmgWhoCares e) {
    e.printStackTrace();
}

В графическом интерфейсе или автоматизированной программе напечатанное сообщение не будет видно. Хуже того, он запускается вместе с остальной частью кода после исключения. Разве исключение не является ошибкой? Тогда не печатайте это. Иначе что-то еще может взорваться через мгновение, и к этому времени исходный объект исключения исчезнет. Эта идиома не лучше, чем BASIC On Error Resume Nextили PHP error_reporting(0);.

Вызов какого-то класса logger не намного лучше:

try {
    ...
} catch (SomethingWeird e) {
    logger.log(e);
}

Это так же лениво, как e.printStackTrace();и до сих пор с кодом в неопределенном состоянии. Кроме того, выбор конкретной системы ведения журналов или другого обработчика зависит от приложения, поэтому это мешает повторному использованию кода.

Но ждать! Существует простой и универсальный способ найти обработчик для конкретного приложения. Он выше стека вызовов (или он установлен как обработчик необработанных исключений потока ). Так что в большинстве мест все, что вам нужно сделать, это выбросить исключение выше в стек . Например, throw e;. Проверенные исключения просто мешают.

Я уверен, что проверенные исключения звучали как хорошая идея при разработке языка, но на практике я обнаружил, что все они беспокоят и не приносят никакой пользы.

50
13.11.2014 18:51:37
Для вашего метода размера с помощью WidgetList я бы кешировал размер в переменной и установил его в конструкторе. Конструктор может создать исключение. Это не будет работать, если файл изменяется при использовании WidgetList, что, вероятно, будет плохо, если это произойдет.
TofuBeer 27.11.2013 03:20:47
SomeStupidExceptionOmgWhoCare хорошо кто-то заботился достаточно, чтобы бросить его. Так что либо он никогда не должен был быть брошен (плохой дизайн), либо вы должны действительно справиться с этим. То же самое относится и к плохой реализации класса до 1.0 (выходной поток байтового массива), где дизайн был, к сожалению, плохим.
TofuBeer 27.11.2013 03:22:06
Правильная идиома была бы директивой, которая ловила бы любые указанные исключения, генерируемые вложенными вызовами подпрограмм, и перебрасывала их, завернутые в a RuntimeException. Обратите внимание, что подпрограмма может быть одновременно объявлена ​​как throws IOExceptionи, в то же время, также указывать, что любой IOExceptionвыброс из вложенного вызова следует считать неожиданным и перенесенным.
supercat 26.12.2013 03:13:43
Я профессиональный разработчик C # с некоторым опытом Java, который наткнулся на этот пост. Я озадачен тем, почему кто-то поддержит это странное поведение. В .NET, если я хочу перехватить определенный тип исключения, я могу это перехватить. Если я просто хочу, чтобы его выбросили в стек, ничего не поделаешь. Я бы хотел, чтобы Java не была такой причудливой. :)
aikeru 15.03.2014 04:38:55
Что касается «иногда я закомментирую вызов метода временно» - я научился использовать if (false)для этого. Это позволяет избежать проблемы с оператором throw, а предупреждение помогает мне быстрее вернуться назад. +++ Тем не менее, я согласен со всем, что вы написали. Проверенные исключения имеют некоторое значение, но это значение незначительно по сравнению с их стоимостью. Почти всегда они просто мешают.
maaartinus 25.05.2017 04:03:23

Проверенные исключения в своей первоначальной форме были попыткой обработки непредвиденных обстоятельств, а не сбоев. Похвальной целью было выделить конкретные предсказуемые точки (невозможно подключиться, файл не найден и т. Д.) И убедиться, что разработчики справились с ними.

То, что никогда не было включено в первоначальную концепцию, заключалось в том, чтобы принудительно объявлять широкий спектр системных и неисправимых отказов. Эти сбои никогда не были правильными, чтобы быть объявленными как проверенные исключения.

Сбои обычно возможны в коде, и контейнеры EJB, web и Swing / AWT уже учитывают это, предоставляя внешний обработчик исключений «сбой запроса». Самая основная правильная стратегия - откатить транзакцию и вернуть ошибку.

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

Самый большой аргумент против «проверенных» исключений заключается в том, что большинство исключений нельзя исправить. Простой факт в том, что мы не владеем кодом / подсистемой, которая сломалась. Мы не видим реализацию, мы не несем за нее ответственность и не можем ее исправить.

Если наше приложение не является БД ... мы не должны пытаться исправить БД. Это нарушило бы принцип инкапсуляции .

Особенно проблематичными были области JDBC (SQLException) и RMI для EJB (RemoteException). Вместо определения исправляемых непредвиденных обстоятельств согласно первоначальной концепции «проверенных исключений» эти широко распространенные проблемы вынужденной системной надежности, которые на самом деле не поддаются устранению, должны быть широко заявлены.

Другой серьезный недостаток в дизайне Java заключался в том, что обработка исключений должна быть правильно размещена на максимально возможном уровне «бизнес» или «запрос». Принцип здесь "бросай рано, лови поздно". Проверенные исключения мало что делают, но мешают этому.

В Java существует очевидная проблема, заключающаяся в том, что требуются тысячи бесполезных блоков try-catch, при этом значительная часть (40% +) неправильно кодируется. Почти ни один из них не реализует никакой подлинной обработки или надежности, но накладывает большие накладные расходы на кодирование.

Наконец, «проверенные исключения» в значительной степени несовместимы с функциональным программированием на FP.

Их настойчивое требование «немедленно обрабатывать» противоречит как передовой практике «обработки с опозданием», так и любой структуре FP, которая абстрагирует циклы / или поток управления.

Многие люди говорят об «обработке» проверенных исключений, но говорят через свои шляпы. Продолжение после сбоя с нулевыми, неполными или неправильными данными, чтобы притвориться успешным, ничего не обрабатывает. Это халатность инженерии / надежности самой низкой формы.

Чистая неудача - самая основная правильная стратегия обработки исключения. Откат транзакции, регистрация ошибки и сообщение пользователю о «неудачном» ответе - разумная практика, и самое главное, предотвращение передачи неверных бизнес-данных в базу данных.

Другими стратегиями обработки исключений являются «повтор», «переподключение» или «пропуск» на уровне бизнеса, подсистемы или уровня запросов. Все это общие стратегии надежности, и они работают хорошо / лучше с исключениями времени выполнения.

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

Смотрите:
- http://literatejava.com/exceptions/checked-exceptions-javas-biggest-mistake/

4
2.06.2014 03:34:41
Моя точка зрения заключается в том, чтобы как следует потерпеть неудачу как общая стратегия. Непроверенные исключения помогают этому, поскольку они не заставляют блоки захвата вставляться. Захват и ведение журнала ошибок могут быть оставлены на усмотрение нескольких внешних обработчиков, а не неправильно кодированы тысячи раз по всей кодовой базе (что на самом деле скрывает ошибки) . Для произвольных сбоев непроверенные исключения являются абсолютно наиболее правильными. Непредвиденные обстоятельства - предсказуемые результаты, такие как нехватка средств - являются единственными законными исключениями, которые стоит проверить.
Thomas W 11.06.2015 23:56:50
Мой ответ уже выше обращается к этому. В первую очередь, 1) Внешний обработчик ошибок должен поймать все. Кроме того, только для определенных идентифицированных сайтов, 2) Конкретные ожидаемые непредвиденные обстоятельства могут быть обнаружены и обработаны - на ближайшем сайте они выбрасываются. Это означает, что файл не найден, недостаточно средств и т. Д. В момент, когда они могут быть восстановлены - не выше. Принцип инкапсуляции означает, что внешние слои не могут / не должны нести ответственность за понимание / восстановление после сбоев глубоко внутри. В-третьих, 3) все остальное должно быть выброшено наружу - по возможности не проверено.
Thomas W 15.06.2015 01:27:14
Самый внешний обработчик перехватывает Exception, регистрирует его и возвращает «сбойный» ответ или отображает диалоговое окно с сообщением об ошибке. Очень просто, совсем не сложно определить. Дело в том, что каждое исключение, которое может быть восстановлено не сразу и локально, является неисправимым отказом из-за принципа инкапсуляции. Если код, о котором нужно знать, не может его восстановить, тогда запрос в целом завершается неудачно и правильно. Это правильный способ сделать это правильно.
Thomas W 15.06.2015 01:34:52
Неправильно. Задача внешнего обработчика состоит в том, чтобы аккуратно завершать работу с ошибками и регистрировать ошибки на границе запроса. Сломанный запрос завершается неудачно, об исключении сообщается, поток может продолжить обслуживание следующего запроса. Такой внешний обработчик является стандартной функцией в контейнерах Tomcat, AWT, Spring, EJB и «основном» потоке Java.
Thomas W 17.06.2015 23:23:53
Почему опасно сообщать о «подлинных ошибках» на границе запроса или крайнем обработчике ??? Я часто работаю в области системной интеграции и надежности, где правильное проектирование надежности действительно важно, и использую подходы «непроверенного исключения» для этого. Я не совсем уверен, что вы на самом деле обсуждаете - кажется, что вы, возможно, захотите провести 3 месяца в неконтролируемом порядке исключения, почувствовать это, а затем, возможно, мы сможем обсудить дальше. Спасибо.
Thomas W 25.06.2015 04:23:23

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

Тонкая выгода перевешивается обременительными затратами (особенно в больших, менее гибких базах кода, где постоянное изменение сигнатур интерфейса нецелесообразно).

Статический анализ может быть хорошим, но действительно надежный статический анализ часто жестко требует от программиста строгой работы. Существует расчет затрат и выгод, и для проверки, которая приводит к ошибке времени компиляции, необходимо установить высокую планку. Было бы более полезно, если бы IDE взяла на себя роль сообщения о том, какие исключения может вызвать метод (в том числе и те, которые неизбежны). Хотя, возможно, это не было бы столь же надежно без принудительных объявлений исключений, большинство исключений все равно будет объявлено в документации, и надежность предупреждения IDE не так важна.

3
16.07.2015 02:15:35