Уже несколько лет я не могу получить достойный ответ на следующий вопрос: почему некоторые разработчики так против проверенных исключений? У меня было много разговоров, читал что-то в блогах, читал то, что говорил Брюс Экель (первый человек, которого я видел, выступал против них).
В настоящее время я пишу новый код и уделяю очень пристальное внимание тому, как я работаю с исключениями. Я пытаюсь увидеть точку зрения толпы «нам не нравятся проверенные исключения», и я до сих пор ее не вижу.
Каждый мой разговор заканчивается тем, что один и тот же вопрос остается без ответа ... позвольте мне настроить его:
В общем (от того, как Java был разработан),
Error
для вещей, которые никогда не должны быть пойманы (у VM есть аллергия на арахис, и кто-то уронил банку с арахисом на это)RuntimeException
для вещей, которые программист сделал неправильно (программист ушел от конца массива)Exception
(кромеRuntimeException
) для вещей, которые находятся вне контроля программиста (диск заполняется при записи в файловую систему, достигнут предел дескриптора файла для процесса, и вы больше не можете открывать файлы)Throwable
просто родитель всех типов исключений.
Обычный аргумент, который я слышу, заключается в том, что если произойдет исключение, то все, что разработчик собирается сделать, это выйти из программы.
Еще один распространенный аргумент, который я слышу, состоит в том, что проверенные исключения затрудняют рефакторинг кода.
В качестве аргумента «все, что я собираюсь сделать, это выйти», я говорю, что даже если вы выходите, вам нужно отобразить разумное сообщение об ошибке. Если вы просто пытаетесь справиться с ошибками, ваши пользователи не будут очень рады, когда программа выйдет без четкого указания, почему.
Для толпы «это затрудняет рефакторинг», это указывает на то, что правильный уровень абстракции не был выбран. Вместо того чтобы объявлять метод, который выдает метод IOException
, IOException
следует преобразовать исключение, которое больше подходит для происходящего.
У меня нет проблем с переносом Main с catch(Exception)
(или в некоторых случаях, catch(Throwable)
чтобы программа могла корректно завершить работу - но я всегда ловлю конкретные исключения, которые мне нужны. Это позволяет мне, по крайней мере, отображать соответствующие сообщение об ошибке.
Вопрос, на который люди никогда не отвечают, заключается в следующем:
Если вы бросаете
RuntimeException
подклассы вместоException
подклассов, то как вы узнаете, что вы должны ловить?
Если ответ улов, Exception
то вы также имеете дело с ошибками программиста так же, как системные исключения. Это кажется мне неправильным.
Если вы перехватываете, Throwable
вы обрабатываете системные исключения и ошибки ВМ (и тому подобное) одинаково. Это кажется мне неправильным.
Если ответ таков, что вы ловите только те исключения, которые, как вы знаете, выбрасываются, то как вы узнаете, какие из них выбрасываются? Что происходит, когда программист X выдает новое исключение и забывает его поймать? Это кажется мне очень опасным.
Я бы сказал, что программа, отображающая трассировку стека, неверна. Люди, которые не любят проверенные исключения, не чувствуют себя так?
Итак, если вам не нравятся отмеченные исключения, можете ли вы объяснить, почему нет И ответьте на вопрос, на который нет ответа, пожалуйста?
Редактировать: я не ищу совета о том, когда использовать какую-либо модель, я ищу то, почему люди расширяются, RuntimeException
потому что им не нравится расширяться Exception
и / или почему они ловят исключение, а затем отбрасывают, RuntimeException
а не добавляют броски в их метод. Я хочу понять мотивацию неприязни к проверенным исключениям.
источник
Ответы:
Я думаю, что прочитал то же интервью с Брюсом Экелом, что и вы, и оно всегда меня раздражало. Фактически, аргумент был сделан интервьюируемым (если это действительно тот пост, о котором вы говорите) Андерсом Хейлсбергом, гением MS, стоящим за .NET и C #.
Несмотря на то, что я из Хейлсберга и его работ, этот аргумент всегда казался мне фальшивым. Это в основном сводится к:
Под «иным способом представляемым пользователю» я подразумеваю, что если вы используете исключение времени выполнения, ленивый программист просто проигнорирует его (вместо того, чтобы перехватывать его пустым блоком catch), и пользователь увидит его.
Краткое изложение этого аргумента сводится к тому, что «Программисты не будут использовать их должным образом, и неправильное их использование хуже, чем отсутствие их» .
В этом аргументе есть доля правды, и на самом деле, я подозреваю, что мотивация Гослинга не помещать переопределения операторов в Java исходит из аналогичного аргумента - они вводят программиста в заблуждение, потому что им часто злоупотребляют.
Но, в конце концов, я нахожу это фиктивным аргументом Хейлсберга и, возможно, постфактумным аргументом, созданным для объяснения недостатка, а не продуманным решением.
Я бы сказал, что хотя чрезмерное использование проверенных исключений является плохой вещью и ведет к небрежной обработке пользователями, но правильное их использование позволяет программисту API дать большую пользу клиентскому программисту API.
Теперь программист API должен быть осторожен, чтобы не создавать проверенные исключения повсеместно, иначе они просто будут раздражать программиста клиента.
(Exception) {}
Хейлсберг предупреждает, что очень ленивый клиент-программист прибегнет к ловле, и вся польза будет потеряна, и наступит ад. Но в некоторых обстоятельствах просто нет замены хорошему проверенному исключению.Для меня классическим примером является API открытия файлов. Каждый язык программирования в истории языков (по крайней мере, в файловых системах) имеет где-то API, который позволяет открывать файл. И каждый клиентский программист, использующий этот API, знает, что им приходится иметь дело с тем случаем, что файл, который они пытаются открыть, не существует. Позвольте мне перефразировать это: каждый клиентский программист, использующий этот API, должен знать, что он должен иметь дело с этим делом. И тут есть одна загвоздка: могут ли программисты API помочь им узнать, что им следует справиться с этим, оставив только комментарии, или они действительно настаивают, чтобы клиент справился с этим.
В Си идиома идет что-то вроде
где
fopen
указывает на ошибку, возвращая 0, а C (по глупости) позволяет вам рассматривать 0 как логическое значение и ... В основном, вы изучаете эту идиому и все в порядке. Но что, если ты новичок и ты не выучил идиому. Тогда, конечно, вы начинаете си учиться трудному пути.
Обратите внимание, что здесь речь идет только о строго типизированных языках: есть четкое представление о том, что такое API в строго типизированном языке: это шведский стол функциональности (методов), который вы можете использовать с четко определенным протоколом для каждого из них.
Этот четко определенный протокол обычно определяется сигнатурой метода. Здесь fopen требует, чтобы вы передали ему строку (или символ * в случае C). Если вы дадите ему что-то еще, вы получите ошибку во время компиляции. Вы не следовали протоколу - вы не используете API должным образом.
В некоторых (малоизвестных) языках тип возвращаемого значения также является частью протокола. Если вы попытаетесь вызвать эквивалент
fopen()
в некоторых языках без присвоения его переменной, вы также получите ошибку во время компиляции (вы можете сделать это только с помощью функций void).Я пытаюсь подчеркнуть следующее: на языке со статической типизацией программист API рекомендует клиенту правильно использовать API, предотвращая компиляцию клиентского кода, если он допускает какие-либо очевидные ошибки.
(В динамически типизированном языке, таком как Ruby, вы можете передать в качестве имени файла что угодно, скажем, float, и он скомпилируется. Зачем беспокоить пользователя проверенными исключениями, если вы даже не собираетесь контролировать аргументы метода. приведенные здесь аргументы применимы только к языкам со статической типизацией.)
Итак, что насчет проверенных исключений?
Вот один из API-интерфейсов Java, который вы можете использовать для открытия файла.
Видишь этот улов? Вот подпись для этого метода API:
Обратите внимание, что
FileNotFoundException
это проверенное исключение.Программист API говорит вам следующее: «Вы можете использовать этот конструктор для создания нового FileInputStream, но вы
а) должен передать имя файла в виде строки
б) должен принять возможность того, что файл может быть не найден во время выполнения "
И это все, что касается меня.
Ключевым моментом является то, что этот вопрос гласит как «Вещи, которые находятся вне контроля программиста». Моей первой мыслью было, что он / она имеет в виду вещи, которые находятся вне контроля программистов API . Но на самом деле, проверенные исключения при правильном использовании должны действительно относиться к вещам, которые находятся вне контроля как программиста клиента, так и программиста API. Я думаю, что это ключ к тому, чтобы не злоупотреблять проверенными исключениями.
Я думаю, что открытие файла прекрасно иллюстрирует суть. Программист API знает, что вы можете дать им имя файла, которое оказывается несуществующим во время вызова API, и что они не смогут вернуть вам то, что вы хотели, но им придется выбросить исключение. Они также знают, что это будет происходить довольно регулярно и клиентский программист может ожидать, что имя файла будет правильным во время написания вызова, но это может быть неправильно во время выполнения по независящим от них причинам.
Итак, API делает это явным: будут случаи, когда этот файл не будет существовать в то время, когда вы мне звоните, и вам, черт побери, лучше с этим справиться.
Это было бы яснее с контр-кейсом. Представьте, что я пишу таблицу API. У меня есть модель таблицы где-то с API, включая этот метод:
Теперь, как программист API, я знаю, что будут случаи, когда некоторый клиент передает отрицательное значение для строки или значение строки вне таблицы. Поэтому у меня может возникнуть соблазн сгенерировать проверенное исключение и заставить клиента с ним справиться:
(Конечно, я бы не назвал это «Проверено».)
Это плохое использование проверенных исключений. Код клиента будет полон вызовов для извлечения данных строки, каждый из которых должен будет использовать try / catch, и для чего? Собираются ли они сообщить пользователю, что был найден неправильный ряд? Вероятно, нет - потому что, каким бы ни был пользовательский интерфейс, окружающий мое табличное представление, он не должен позволять пользователю переходить в состояние, когда запрашивается недопустимая строка. Так что это ошибка со стороны клиентского программиста.
Программист API все еще может предсказать, что клиент будет кодировать такие ошибки и должен обрабатывать их с исключением времени выполнения, например
IllegalArgumentException
.С проверенным исключением
getRowData
, это явно тот случай, который приведет к тому, что ленивый программист Хейлсберга просто добавит пустые уловы. Когда это происходит, недопустимые значения строк не будут очевидны даже для отладчика тестировщика или разработчика клиента, скорее, они приведут к ошибкам, которые трудно определить источник. Ракеты Arianne взорвутся после запуска.Итак, вот в чем проблема: я говорю, что проверенное исключение
FileNotFoundException
- это не просто хорошая вещь, а необходимый инструмент в наборе инструментов для программистов API, который позволяет определить API наиболее полезным способом для программиста клиента. НоCheckedInvalidRowNumberException
это большое неудобство, приводящее к плохому программированию, и его следует избегать. Но как сказать разницу.Я полагаю, что это не точная наука, и я полагаю, что она лежит в основе и, возможно, в определенной степени оправдывает аргументацию Хейлсберга. Но я не рад выбрасывать ребенка с водой, поэтому позвольте мне извлечь некоторые правила, чтобы отличить проверенные исключения от плохих:
Вне контроля клиента или Закрыто против Открытого:
Проверенные исключения следует использовать только в том случае, если ошибка не контролируется как API, так и клиентским программистом. Это связано с тем, насколько открыта или закрыта система. В ограниченном пользовательском интерфейсе, где клиентский программист имеет контроль, скажем, над всеми кнопками, командами клавиатуры и т. Д., Которые добавляют и удаляют строки из табличного представления (закрытая система), это ошибка программирования клиента, если он пытается извлечь данные из несуществующий ряд В файловой операционной системе, где любое количество пользователей / приложений может добавлять и удалять файлы (открытая система), вполне возможно, что файл, запрашиваемый клиентом, был удален без их ведома, поэтому следует ожидать, что он будет иметь с ним дело. ,
Ubiquity:
Проверенные исключения не должны использоваться в вызове API, который часто выполняется клиентом. Под часто я имею в виду много мест в клиентском коде - не часто во времени. Поэтому клиентский код не склонен многократно открывать один и тот же файл, но мое табличное представление встречается
RowData
везде разными способами. В частности, я собираюсь написать много кода, каки будет больно каждый раз оборачиваться в try / catch.
Информирование пользователя:
Проверенные исключения следует использовать в тех случаях, когда вы можете представить полезное сообщение об ошибке, которое будет представлено конечному пользователю. Это "а что ты будешь делать, когда это произойдет?" вопрос, который я поднял выше. Это также относится к пункту 1. Поскольку вы можете предсказать, что что-то за пределами вашей системы клиент-API может привести к отсутствию файла, вы можете разумно сообщить об этом пользователю:
Так как ваш неправильный номер строки был вызван внутренней ошибкой и не по вине пользователя, на самом деле нет никакой полезной информации, которую вы можете им дать. Если ваше приложение не позволяет исключениям среды выполнения попадать на консоль, оно, вероятно, в конечном итоге выдаст им какое-то уродливое сообщение вроде:
Короче говоря, если вы не думаете, что ваш клиентский программист может объяснить ваше исключение так, чтобы это помогло пользователю, то вам, вероятно, не следует использовать проверенное исключение.
Таковы мои правила. Несколько придумано, и, несомненно, будут исключения (пожалуйста, помогите мне уточнить их, если хотите). Но мой главный аргумент в том, что есть такие случаи, как
FileNotFoundException
когда проверяемое исключение является такой же важной и полезной частью контракта API, как и типы параметров. Поэтому мы не должны отказываться от него только потому, что он используется не по назначению.Извините, я не хотел делать это так долго и вафельно. Позвольте мне закончить с двумя предложениями:
A: Программисты API: экономно используйте проверенные исключения, чтобы сохранить их полезность. В случае сомнений используйте непроверенное исключение.
B: Клиентские программисты: привыкайте создавать упакованное исключение (Google google) на ранней стадии разработки. JDK 1.4 и более поздние версии предоставляют конструктор
RuntimeException
для этого, но вы также можете легко создать и свой собственный. Вот конструктор:Затем приобретите привычку всякий раз, когда вам нужно обработать проверенное исключение, и вы чувствуете себя ленивым (или вы думаете, что программист API слишком усердно использовал проверенное исключение в первую очередь), не просто проглотите исключение, оберните его и отбросить его.
Поместите это в один из маленьких шаблонов кода вашей IDE и используйте его, когда вам лень. Таким образом, если вам действительно нужно обработать проверенное исключение, вы будете вынуждены вернуться и разобраться с ним, увидев проблему во время выполнения. Потому что, поверьте мне (и Андерсу Хейлсбергу), вы никогда не вернетесь к этому TODO в своем
источник
Суть проверяемых исключений в том, что они не являются исключениями при обычном понимании концепции. Вместо этого они являются альтернативными возвращаемыми значениями API.
Вся идея исключений заключается в том, что ошибка, выдаваемая где-то далеко вниз по цепочке вызовов, может всплывать и обрабатываться кодом где-то дальше, без вмешательства кода. Проверенные исключения, с другой стороны, требуют, чтобы каждый уровень кода между метателем и перехватчиком объявлял, что они знают обо всех формах исключения, которые могут пройти через них. На практике это практически не отличается от того, если проверенные исключения были просто специальными возвращаемыми значениями, которые должен был проверять вызывающий объект. например, [псевдокод].:
Поскольку Java не может использовать альтернативные возвращаемые значения или простые встроенные кортежи в качестве возвращаемых значений, проверенные исключения являются разумным ответом.
Проблема заключается в том, что большая часть кода, в том числе большие наборы стандартной библиотеки, неправильно использует проверенные исключения для реальных исключительных условий, которые вы вполне можете захотеть поднять на несколько уровней. Почему IOException не является RuntimeException? На любом другом языке я могу допустить исключение ввода-вывода, и если я ничего не сделаю для его обработки, мое приложение остановится, и я получу удобную трассировку стека для просмотра. Это лучшее, что может случиться.
Возможно, двумя способами из примера вы хотите перехватить все исключения IOException из всего процесса записи в поток, прервать процесс и перейти к коду сообщения об ошибках; в Java вы не можете сделать это без добавления «throws IOException» на каждом уровне вызова, даже на уровнях, которые сами по себе не выполняют IO. Такие методы не должны знать об обработке исключений; добавление исключений в их подписи:
И еще есть множество просто смешных библиотечных исключений, таких как:
Когда вам приходится загромождать свой код с помощью такого смешного кода, неудивительно, что проверенные исключения получают массу ненависти, хотя на самом деле это всего лишь простой плохой API-дизайн.
Другой конкретный плохой эффект связан с инверсией управления, когда компонент A передает обратный вызов универсальному компоненту B. Компонент A хочет иметь возможность разрешить исключение из своего обратного вызова обратно в то место, где он вызвал компонент B, но он не может потому что это изменило бы интерфейс обратного вызова, который исправлен B. A может сделать это, только обернув реальное исключение в RuntimeException, который является еще одним шаблоном обработки исключений для записи.
Проверенные исключения, реализованные в Java и его стандартной библиотеке, означают шаблон, шаблон, шаблон. На и без того многословном языке это не победа.
источник
Вместо того, чтобы перефразировать все (многие) причины против проверенных исключений, я выберу только одну. Я потерял счетчик количества написаний этого блока кода:
99% времени я ничего не могу с этим поделать. Наконец блоки делают любую необходимую очистку (или, по крайней мере, они должны).
Я также потерял счет количество раз, я видел это:
Почему? Потому что кому-то приходилось иметь с этим дело и было лень. Было ли это неправильно? Конечно. Это бывает? Абсолютно. Что если бы это было неконтролируемое исключение? Приложение бы просто умерло (что предпочтительнее, чем проглатывание исключения).
И затем у нас есть бешеный код, который использует исключения в качестве формы управления потоком, например, java.text.Format делает . Bzzzt. Неправильно. Пользователь, помещающий «abc» в числовое поле на форме, не является исключением.
Хорошо, я думаю, это было три причины.
источник
Я знаю, что это старый вопрос, но я боролся с проверенными исключениями, и мне есть что добавить. Пожалуйста, прости меня за это!
Моя главная проблема с проверенными исключениями в том, что они разрушают полиморфизм. Невозможно заставить их играть красиво с полиморфными интерфейсами.
Возьми добрую Яву
List
интерфейс . У нас есть общие реализации в памяти, такие какArrayList
иLinkedList
. У нас также есть класс скелета,AbstractList
который позволяет легко создавать новые типы списков. Для списка только для чтения нам нужно реализовать только два метода:size()
иget(int index)
.Этот пример
WidgetList
класса читает некоторые объекты фиксированного размера типаWidget
(не показаны) из файла:Предоставляя виджеты, используя знакомый
List
интерфейс, вы можете получить элементы (list.get(123)
) или выполнить итерацию по списку (for (Widget w : list) ...
) без необходимости знать оWidgetList
себе. Можно передать этот список любым стандартным методам, которые используют общие списки, или обернуть его вCollections.synchronizedList
. Коду, который его использует, не нужно ни знать, ни заботиться о том, создаются ли «виджеты» на месте, поступают ли они из массива, читаются ли они из файла, базы данных, по всей сети или из будущего ретранслятора подпространства. Он все равно будет работать правильно, потому чтоList
интерфейс реализован правильно.За исключением того, что это не так. Приведенный выше класс не компилируется, потому что методы доступа к файлу могут выдавать
IOException
проверенное исключение, которое вы должны «поймать или указать». Вы не можете указать его как выброшенный - компилятор не позволит вам, потому что это нарушит контрактList
интерфейса. И нет никакого полезного способа, которымWidgetList
сам мог бы обработать исключение (как я объясню позже).Очевидно, единственное, что нужно сделать, это перехватить и перебросить проверенные исключения как неконтролируемое исключение:
((Редактировать: Java 8 добавила
UncheckedIOException
класс именно для этого случая: для ловли и повторного бросанияIOException
s через границы полиморфных методов. Это доказывает мою точку зрения!))Так что проверенные исключения просто не работают в подобных случаях. Вы не можете бросить их. То же самое для умного,
Map
поддерживаемого базой данных, или реализацияjava.util.Random
связанной с источником квантовой энтропии через COM-порт. Как только вы пытаетесь сделать что-то новое с использованием полиморфного интерфейса, концепция проверенных исключений не срабатывает. Но проверенные исключения настолько коварны, что они все равно не оставят вас в покое, потому что вам все равно придется перехватывать и отбрасывать любые из методов более низкого уровня, загромождая код и загромождая трассировку стека.Я считаю, что вездесущий
Runnable
интерфейс часто возвращается в этот угол, если он вызывает что-то, что вызывает проверенные исключения. Он не может генерировать исключение как есть, поэтому все, что он может сделать, это загромождать код, перехватывая и перебрасывая какRuntimeException
.На самом деле, вы можете бросить необъявленные проверенные исключения, если вы прибегаете к взлому. Во время выполнения JVM не заботится о проверенных правилах исключений, поэтому нам нужно обмануть только компилятор. Самый простой способ сделать это - использовать дженерики. Это мой метод для него (имя класса показано, потому что (до Java 8) это требуется в синтаксисе вызова для универсального метода):
Ура! Используя это, мы можем выбросить проверенное исключение на любую глубину стека, не объявляя его, не оборачивая его
RuntimeException
и не загромождая трассировку стека! Используя пример «WidgetList» снова:К сожалению, последнее оскорбление проверенных исключений состоит в том, что компилятор отказывается позволить вам перехватить проверенное исключение, если, по его ошибочному мнению, оно не могло быть сгенерировано. (Непроверенные исключения не имеют этого правила.) Чтобы поймать скрытно выброшенное исключение, мы должны сделать это:
Это немного неудобно, но, с другой стороны, он все же немного проще, чем код для извлечения проверенного исключения, которое было заключено в
RuntimeException
.К счастью, здесь
throw t;
утверждение является законным, хотя типt
проверяется , благодаря правилу, добавленному в Java 7 о повторном отбрасывании перехваченных исключений.Когда проверенные исключения встречаются с полиморфизмом, возникает и обратный случай: когда метод специфицируется как потенциально вызывающий исключение, а переопределенная реализация - нет. Например, абстрактный класс
OutputStream
«Swrite
методы все указатьthrows IOException
.ByteArrayOutputStream
является подклассом, который пишет в массив в памяти вместо истинного источника ввода / вывода. Его переопределенныеwrite
методы не могут вызыватьIOException
s, поэтому они не имеютthrows
предложения, и вы можете вызывать их, не беспокоясь о требовании «поймать или указать».Кроме не всегда. Предположим, что
Widget
есть метод для сохранения его в поток:Объявление этого метода для принятия простого
OutputStream
- правильное решение, поэтому его можно использовать полиморфно со всеми видами выходных данных: файлы, базы данных, сеть и т. Д. И массивы в памяти. Однако для массива в памяти существует ложное требование обрабатывать исключение, которое на самом деле не может произойти:Как обычно, мешают проверенные исключения. Если ваши переменные объявлены как базовый тип, который имеет более открытые требования к исключениям, вы должны добавить обработчики для этих исключений, даже если вы знаете, что они не возникнут в вашем приложении.
Но подождите, проверенные исключения на самом деле настолько раздражают, что даже не позволят вам сделать обратное! Представьте, что вы в настоящее время ловите любые
IOException
броски поwrite
вызовамOutputStream
, но вы хотите изменить объявленный тип переменной на aByteArrayOutputStream
, компилятор будет ругать вас за попытку отловить проверенное исключение, которое, по его словам, не может быть выброшено.Это правило вызывает некоторые абсурдные проблемы. Например, один из трех
write
методовOutputStream
является не переопределеныByteArrayOutputStream
. В частности,write(byte[] data)
это удобный метод, который записывает полный массив путем вызоваwrite(byte[] data, int offset, int length)
со смещением 0 и длиной массива.ByteArrayOutputStream
переопределяет метод с тремя аргументами, но наследует метод удобства с одним аргументом как есть. Унаследованный метод делает абсолютно правильные вещи, но он содержит нежелательноеthrows
предложение. Это было, возможно, упущение в разработкеByteArrayOutputStream
, но они никогда не смогут это исправить, потому что это нарушит совместимость исходного кода с любым кодом, который перехватывает исключение - исключение, которое никогда не было, никогда не будет выброшено!Это правило раздражает и во время редактирования и отладки. Например, иногда я временно закомментирую вызов метода, и если бы он мог вызвать проверенное исключение, компилятор теперь будет жаловаться на существование локальных
try
иcatch
блоков. Так что я тоже должен это закомментировать, и теперь при редактировании кода внутри IDE отступ будет на неправильный уровень, потому что{
и}
закомментированы. Г! Это небольшая жалоба, но кажется, что единственное, что проверенные исключения когда-либо делают, это вызывает проблемы.Я почти закончил. Последнее разочарование в связи с проверенными исключениями заключается в том, что на большинстве сайтов вызовов нет ничего полезного, что вы можете с ними сделать. В идеале, когда что-то идет не так, у нас должен быть компетентный специфичный для приложения обработчик, который может сообщить пользователю о проблеме и / или завершить или повторить операцию в зависимости от ситуации. Это может сделать только обработчик, находящийся высоко в стеке, потому что он единственный, кто знает общую цель.
Вместо этого мы получаем следующую идиому, которая широко используется как способ закрыть компилятор:
В графическом интерфейсе или автоматизированной программе напечатанное сообщение не будет видно. Хуже того, он запускается вместе с остальным кодом после исключения. Разве исключение не является ошибкой? Тогда не печатайте это. Иначе что-то еще может взорваться через мгновение, и к этому времени исходный объект исключения исчезнет. Эта идиома не лучше, чем BASIC
On Error Resume Next
или PHPerror_reporting(0);
.Вызов какого-то класса logger не намного лучше:
Это так же лениво, как
e.printStackTrace();
и до сих пор с кодом в неопределенном состоянии. Кроме того, выбор конкретной системы ведения журналов или другого обработчика зависит от приложения, поэтому это мешает повторному использованию кода.Но ждать! Существует простой и универсальный способ найти обработчик для конкретного приложения. Он выше стека вызовов (или он установлен как обработчик необработанных исключений потока ). Так что в большинстве мест все, что вам нужно сделать, это выбросить исключение выше в стек . Например,
throw e;
. Проверенные исключения просто мешают.Я уверен, что проверенные исключения звучали как хорошая идея, когда разрабатывался язык, но на практике я обнаружил, что все они беспокоят и не принесут никакой пользы.
источник
RuntimeException
. Обратите внимание, что подпрограмма может быть одновременно объявлена какthrows IOException
и, в то же время, также указывать, что любойIOException
выброс из вложенного вызова следует считать неожиданным и перенесенным.if (false)
для этого. Это позволяет избежать проблемы с предложением броска, а предупреждение помогает мне быстрее вернуться назад. +++ Тем не менее, я согласен со всем, что вы написали. Проверенные исключения имеют некоторое значение, но это значение незначительно по сравнению с их стоимостью. Почти всегда они просто мешают.Ну, речь не идет об отображении трассировки стека или тихом сбое. Речь идет о возможности сообщать об ошибках между слоями.
Проблема с проверенными исключениями заключается в том, что они побуждают людей проглотить важные детали (а именно, класс исключений). Если вы решите не проглатывать эту деталь, то вам придется добавлять объявления бросков по всему вашему приложению. Это означает, 1) что новый тип исключения будет влиять на множество сигнатур функций, и 2) вы можете пропустить конкретный экземпляр исключения, которое вы действительно хотите поймать (скажем, вы открываете дополнительный файл для функции, которая записывает данные в файл. Вторичный файл является необязательным, поэтому вы можете игнорировать его ошибки, но поскольку подпись
throws IOException
, это легко пропустить).Я на самом деле имею дело с этой ситуацией сейчас в приложении. Мы перепаковали почти исключения как AppSpecificException. Это сделало подписи действительно чистыми, и нам не пришлось беспокоиться о взрывах
throws
подписей.Конечно, теперь нам нужно специализировать обработку ошибок на более высоких уровнях, реализуя логику повторных попыток и тому подобное. Все это AppSpecificException, поэтому мы не можем сказать «Если IOException выброшен, повторите попытку» или «Если ClassNotFound выброшен, прервать полностью». У нас нет надежного способа добраться до реального исключения, потому что вещи переупаковываются снова и снова, когда они проходят между нашим кодом и сторонним кодом.
Вот почему я большой поклонник обработки исключений в Python. Вы можете поймать только то, что вы хотите и / или можете справиться. Все остальное пузырится, как будто вы перебрасываете это самостоятельно (что вы сделали в любом случае).
Я снова и снова обнаруживал, что в течение всего проекта, который я упоминал, обработка исключений делится на 3 категории:
источник
throws
объявлений.SNR
Во-первых, проверенные исключения уменьшают «отношение сигнал / шум» для кода. Андерс Хейлсберг также говорит об императивном и декларативном программировании, которое является аналогичной концепцией. В любом случае рассмотрим следующие фрагменты кода:
Обновить пользовательский интерфейс из не-пользовательского потока в Java:
Обновить пользовательский интерфейс из не-пользовательского потока в C #:
Что кажется мне намного понятнее. Когда вы начинаете все больше и больше работать с пользовательским интерфейсом в Swing, проверенные исключения начинают становиться действительно раздражающими и бесполезными.
Побег из тюрьмы
Для реализации даже самых базовых реализаций, таких как интерфейс List в Java, проверены исключения как инструмент для проектирования по контракту. Рассмотрим список, который поддерживается базой данных, файловой системой или любой другой реализацией, которая выдает проверенное исключение. Единственная возможная реализация - перехватить проверенное исключение и повторно выбросить его как исключение без проверки:
И теперь вы должны спросить, в чем смысл всего этого кода? Проверенные исключения только добавляют шум, исключение было поймано, но не обработано, и дизайн по контракту (с точки зрения проверенных исключений) сломался.
Вывод
Я писал об этом ранее .
источник
Artima опубликовала интервью с одним из архитекторов .NET, Андерсом Хейлсбергом, в котором подробно освещены аргументы против проверенных исключений. Короткий дегустатор:
источник
Сначала я согласился с вами, поскольку всегда поддерживал проверенные исключения, и начал задумываться о том, почему мне не нравится не проверять исключения в .Net. Но потом я понял, что я не заражаюсь, как проверенные исключения.
Чтобы ответить на ваш вопрос, да, мне нравятся мои программы, показывающие следы стека, желательно очень уродливые. Я хочу, чтобы приложение взорвалось в ужасную кучу самых уродливых сообщений об ошибках, которые вы когда-либо хотели видеть.
И причина в том, что, если это так, я должен это исправить, и я должен это исправить сразу же. Я хочу сразу знать, что есть проблема.
Сколько раз вы на самом деле обрабатываете исключения? Я не говорю о ловле исключений - я говорю об обработке их? Слишком легко написать следующее:
И я знаю, что вы можете сказать, что это плохая практика, и что «ответ» заключается в том, чтобы сделать что-то с исключением (позвольте мне догадаться, записать это?), Но в реальном мире (тм) большинство программистов просто не делают Это.
Так что да, я не хочу ловить исключения, если мне не нужно это делать, и я хочу, чтобы моя программа эффектно взорвалась, когда я облажался. Молчаливая неудача - худший из возможных результатов.
источник
RuntimeExceptions
что вам действительно не нужно ловить, и я согласен, что вы должны позволить программе взорваться. Исключения, которые вы всегда должны ловить и обрабатывать, - это проверенные исключенияIOException
. Если вы получитеIOException
, в вашем коде нечего исправить; Ваша программа не должна взорваться только потому, что произошел сбой сети.В статье « Эффективные исключения Java» подробно объясняется, когда использовать непроверенные и когда использовать отмеченные исключения. Вот некоторые цитаты из этой статьи, чтобы выделить основные моменты:
(ТАК не позволяет таблицы, поэтому вы можете прочитать следующее с исходной страницы ...)
источник
args[]
? Это непредвиденный случай или ошибка?File#openInputStream
возвращенияEither<InputStream, Problem>
- если ты это имеешь в виду, тогда мы можем согласиться.Короче говоря:
Исключения составляют вопрос разработки API. -- Не больше, не меньше.
Аргумент для проверенных исключений:
Чтобы понять, почему проверенные исключения могут быть не очень хорошими вещами, давайте перевернем вопрос и спросим: когда или почему проверенные исключения привлекательны, то есть, почему вы хотите, чтобы компилятор принудительно применял объявление исключений?
Ответ очевиден: иногда вам нужно перехватить исключение, и это возможно только в том случае, если вызываемый код предлагает определенный класс исключений для интересующей вас ошибки.
Следовательно, аргумент для проверенных исключений заключается в том, что компилятор заставляет программистов объявлять, какие исключения генерируются, и, надеюсь, программист затем также документирует конкретные классы исключений и ошибки, которые их вызывают.
В действительности, однако, слишком часто пакет
com.acme
только бросает,AcmeException
а не определенные подклассы. Затем вызывающие абоненты должны обрабатывать, объявлять или повторно сигнализироватьAcmeExceptions
, но все еще не могут быть уверены,AcmeFileNotFoundError
произошло ли событие илиAcmePermissionDeniedError
.Поэтому, если вы заинтересованы только в
AcmeFileNotFoundError
решении, решение состоит в том, чтобы подать запрос функции программистам ACME и сказать им, чтобы они реализовали, объявили и документировали этот подклассAcmeException
.Так зачем?
Следовательно, даже с проверенными исключениями компилятор не может заставить программистов создавать полезные исключения. Это все еще вопрос качества API.
В результате языки без проверенных исключений обычно не намного хуже. Программисты могут испытывать искушение бросать неопределенные экземпляры общего
Error
класса, а не классаAcmeException
, но если они вообще заботятся о качестве своего API, они научатся вводитьAcmeFileNotFoundError
.В целом, спецификация и документация об исключениях мало чем отличается от спецификации и документации, скажем, обычных методов. Это тоже вопрос разработки API, и если программист забыл внедрить или экспортировать полезную функцию, API необходимо улучшить, чтобы вы могли с ней работать с пользой.
Если вы будете следовать этой линии рассуждений, должно быть очевидно, что «хлопоты» по объявлению, отлову и повторному выбрасыванию исключений, которые так распространены в таких языках, как Java, часто не приносят никакой пользы.
Стоит также отметить, что Java VM не имеет проверенных исключений - только их проверяет компилятор Java, а файлы классов с измененными объявлениями исключений совместимы во время выполнения. Безопасность Java VM не улучшена проверенными исключениями, только стиль кодирования.
источник
AcmeException
а не сAcmeFileNotFoundError
удачей, выясняя, что вы сделали неправильно, и где вам нужно поймать это. Проверенные исключения предоставляют программистам некоторую защиту от плохого дизайна API.В последние три года я работал с несколькими разработчиками в относительно сложных приложениях. У нас есть кодовая база, которая довольно часто использует Checked Exceptions с правильной обработкой ошибок, а другая - нет.
До сих пор мне было проще работать с базой кода с проверенными исключениями. Когда я использую чужой API, приятно видеть, какие именно ошибки можно ожидать, когда я вызываю код и обрабатываю их должным образом, путем регистрации, отображения или игнорирования (да, есть допустимые случаи игнорирования исключения, такие как реализация ClassLoader). Это дает код, который я пишу, возможность восстановить. Все исключения во время выполнения я распространяю до тех пор, пока они не будут кэшированы и обработаны с помощью некоторого общего кода обработки ошибок. Когда я нахожу проверенное исключение, которое на самом деле не хочу обрабатывать на определенном уровне или которое я рассматриваю как ошибку логики программирования, я оборачиваю его в RuntimeException и позволяю ему всплыть. Никогда, никогда не глотайте исключение без уважительной причины (а веских причин для этого довольно мало)
Когда я работаю с базой кода, у которой нет проверенных исключений, мне становится немного сложнее узнать заранее, что я могу ожидать при вызове функции, которая может ужасно сломать некоторые вещи.
Это все, конечно, вопрос предпочтений и навыков разработчика. Оба способа программирования и обработки ошибок могут быть одинаково эффективными (или неэффективными), поэтому я бы не сказал, что есть The One Way.
В общем, мне легче работать с проверенными исключениями, особенно в больших проектах с большим количеством разработчиков.
источник
Категории исключений
Говоря об исключениях, я всегда ссылаюсь на статью в блоге Эрика Липперта об исключениях Vexing . Он помещает исключения в эти категории:
OutOfMemoryError
илиThreadAbortException
.ArrayIndexOutOfBoundsException
,NullPointerException
или любойIllegalArgumentException
.NumberFormatException
изInteger.parseInt
вместо того , обеспечиваяInteger.tryParseInt
метод , который возвращает логическое значение FALSE на синтаксического анализа отказа.FileNotFoundException
.Пользователь API:
Проверенные исключения
Тот факт, что пользователь API должен обрабатывать определенное исключение, является частью контракта метода между вызывающей стороной и вызываемой стороной. В контракте указывается, среди прочего: количество и типы аргументов, которые ожидает вызываемый объект, тип возвращаемого значения, которого может ожидать вызывающий объект, и исключения, которые ожидается от вызывающего объекта .
Так как неприятные исключения не должны существовать в API, только эти экзогенные исключения должны быть проверены, чтобы быть частью контракта метода. Относительно немного исключений являются экзогенными , поэтому любой API должен иметь относительно немного проверенных исключений.
Проверенное исключение - это исключение, которое должно быть обработано . Обрабатывать исключение можно так же просто, как проглотить его. Там! Исключение обработано. Период. Если разработчик хочет справиться с этим, хорошо. Но он не может игнорировать исключение и был предупрежден.
Проблемы API
Но любой API, который проверил неприятные и фатальные исключения (например, JCL), создаст ненужную нагрузку на пользователей API. Такие исключения должны быть обработаны, но либо исключение настолько распространено, что оно не должно быть исключением, либо ничего не может быть сделано при его обработке. И это заставляет разработчиков Java ненавидеть проверенные исключения.
Кроме того, многие API не имеют надлежащей иерархии классов исключений, в результате чего все виды неэкзогенных причин исключений представляются одним проверенным классом исключений (например
IOException
). И это также заставляет разработчиков Java ненавидеть проверенные исключения.Вывод
Экзогенными исключениями являются те, которые не являются вашей ошибкой, не могли быть предотвращены, и которые должны быть обработаны. Они образуют небольшое подмножество всех исключений, которые могут быть выброшены. API должны проверять только экзогенные исключения , а все остальные исключения не проверять . Это улучшит API, уменьшит нагрузку на пользователя API и, следовательно, уменьшит необходимость перехватывать все, проглатывать или отбрасывать непроверенные исключения.
Так что не ненавидьте Java и его проверенные исключения. Вместо этого ненавидите API, которые злоупотребляют проверенными исключениями.
источник
Хорошо ... Проверенные исключения не идеальны и имеют некоторые оговорки, но они действительно служат цели. При создании API существуют конкретные случаи сбоев, которые являются договорными для этого API. Если в контексте языка со строгой статической типизацией, такого как Java, если не используются проверенные исключения, то следует полагаться на специальную документацию и соглашения для передачи возможности ошибки. Это исключает все преимущества, которые компилятор может принести в обработке ошибок, и вы полностью оставлены на волю программистов.
Итак, один удаляет проверенное исключение, такое как было сделано в C #, как тогда можно программно и структурно передать возможность ошибки? Как сообщить клиентскому коду, что такие и такие ошибки могут возникать и что с ними нужно бороться?
Я слышу всевозможные ужасы, когда имеешь дело с проверенными исключениями, они используются не по назначению, это точно, но это непроверенные исключения. Я говорю, подождите несколько лет, когда API сложены на несколько уровней, и вы будете просить о возвращении какого-то структурированного средства для передачи сбоев.
Возьмем случай, когда исключение было сгенерировано где-то в нижней части уровней API и просто всплыло, потому что никто не знал, что эта ошибка может даже произойти, хотя это был тип ошибки, который был очень вероятен при вызове кода. бросил его (FileNotFoundException, например, в отличие от VogonsTrashingEarthExcept ... в этом случае не имеет значения, будем ли мы обрабатывать его или нет, так как с ним ничего не останется).
Многие утверждают, что неспособность загрузить файл была почти всегда концом света для процесса, и он должен умереть ужасной и мучительной смертью. Так что да ... конечно ... хорошо ... вы создаете API для чего-то, и он загружает файл в какой-то момент ... Я, как пользователь указанного API, могу отвечать только ... "Кто, черт возьми, вы решаете, когда мой программа должна вылететь! Конечно, учитывая выбор, где исключения поглощаются и не оставляют следов, или исключение EletroFlabbingChunkFluxManifoldChuggingException с трассировкой стека глубже, чем траншея Марианны, я бы взял последнее без малейших колебаний, но означает ли это, что это желательный способ справиться с исключением ? Разве мы не можем быть где-то посередине, где исключение будет переделываться и переноситься каждый раз, когда оно переходит на новый уровень абстракции, чтобы оно действительно что-то значило?
Наконец, большая часть аргумента, который я вижу: «Я не хочу иметь дело с исключениями, многие люди не хотят иметь дело с исключениями. Проверенные исключения заставляют меня иметь дело с ними, поэтому я ненавижу проверенное исключение». отодвинуть его в пропасть гото ада просто глупо и не хватает юмора и видения.
Если бы мы исключили проверенное исключение, мы могли бы также исключить возвращаемый тип для функций и всегда возвращать переменную «любого типа» ... Это сделало бы жизнь намного проще, не так ли?
источник
Действительно, проверенные исключения, с одной стороны, повышают надежность и корректность вашей программы (вы вынуждены делать правильные объявления ваших интерфейсов - исключения, которые выдает метод, в основном являются специальным типом возврата). С другой стороны, вы сталкиваетесь с проблемой, заключающейся в том, что, поскольку исключения «всплывают», очень часто вам нужно изменить целый ряд методов (всех вызывающих и вызывающих абонентов и т. Д.) При изменении одного исключения. метод бросков.
Проверенные исключения в Java не решают последнюю проблему; C # и VB.NET выбрасывают ребенка с водой.
Хороший подход, который идет по среднему пути, описан в этом документе OOPSLA 2005 (или связанном техническом отчете .)
Короче говоря, это позволяет вам сказать:,
method g(x) throws like f(x)
что означает, что g выбрасывает все исключения f бросков. Вуаля, проверил исключения без проблемы каскадных изменений.Хотя это академическая статья, я бы посоветовал вам прочитать ее (частично), так как она хорошо объясняет преимущества и недостатки проверенных исключений.
источник
Это не аргумент против чистой концепции проверенных исключений, но иерархия классов, которую Java использует для них, - это странное шоу. Мы всегда называем вещи просто «исключениями» - что правильно, потому что спецификация языка называет их так же - но как исключение названо и представлено в системе типов?
Кого
Exception
один воображает? Ну, нет, потому чтоException
s являются исключениями, и аналогично исключения являютсяException
s, за исключением тех исключений, которые не являютсяException
s, потому что на самом деле другие исключения - этоError
s, которые представляют собой исключение другого типа, своего рода исключительное исключение, которое никогда не должно происходить, кроме когда это произойдет, и что вы никогда не должны поймать, за исключением иногда вы должны. За исключением того, что это не все, потому что вы также можете определить другие исключения, которые не являются ниException
s, ниError
s, а простоThrowable
исключениями.Какие из них являются «проверенными» исключениями?
Throwable
s являются проверенными исключениями, за исключением случаев, когда они также являютсяError
s, которые являются непроверенными исключениями, а затем естьException
s, которые также являютсяThrowable
s и являются основным типом проверяемого исключения, за исключением того, что есть и одно исключение, которое заключается в том, что если они такжеRuntimeException
с, потому что это другой вид непроверенного исключения.Для чего
RuntimeException
? Ну, как следует из названия, они являются исключениями, как и всеException
s, и они происходят во время выполнения, как и все исключения на самом деле, за исключением того, чтоRuntimeException
s являются исключительными по сравнению с другимиException
s во время выполнения, потому что они не должны происходить, кроме когда вы делаете какую-то глупую ошибку, хотяRuntimeException
s никогда не бываютError
s, значит, они относятся к вещам, которые являются исключительно ошибочными, но которые на самом деле не являютсяError
s. За исключением тогоRuntimeErrorException
, что на самом деле являетсяRuntimeException
дляError
с. Но разве не все исключения должны представлять ошибочные обстоятельства? Да, все из них. ЗаThreadDeath
исключением исключительно исключительного исключения, поскольку в документации объясняется, что это «нормальное явление», и именно поэтому они сделали его типомError
В любом случае, поскольку мы делим все исключения по середине на
Error
s (которые предназначены для исключительных исключений при выполнении, поэтому не проверены) иException
s (которые предназначены для менее исключительных ошибок при выполнении, поэтому проверяются, за исключением случаев, когда их нет), теперь нам нужно два различные виды каждого из нескольких исключений. Поэтому нам нужныIllegalAccessError
иIllegalAccessException
, иInstantiationError
иInstantiationException
, и,NoSuchFieldError
иNoSuchFieldException
, и,NoSuchMethodError
иNoSuchMethodException
, иZipError
иZipException
.За исключением того, что даже когда проверяется исключение, всегда есть (довольно простые) способы обмануть компилятор и выбросить его без проверки. Если вы сделаете это, вы можете получить
UndeclaredThrowableException
, за исключением других случаев, где его могут вырвать какUnexpectedException
, или какUnknownException
(что не связаноUnknownError
, только для «серьезных исключений»), или какExecutionException
, или какInvocationTargetException
, или какExceptionInInitializerError
.О, и мы не должны забывать о шикарном новом Java 8
UncheckedIOException
, который являетсяRuntimeException
исключением, разработанным, чтобы позволить вам выбросить концепцию проверки исключений из окна, обернув проверенныеIOException
исключения, вызванные ошибками ввода-вывода (которые не вызываютIOError
исключений, хотя и существуют) тоже), с которыми исключительно трудно справиться, и поэтому вам нужно, чтобы их не проверяли.Спасибо Java!
источник
Проблема
Худшая проблема, которую я вижу с механизмом обработки исключений, состоит в том, что он вводит дублирование кода в большом масштабе ! Давайте будем честными: в большинстве проектов в 95% случаев все, что действительно нужно сделать разработчикам, - это как-то сообщить об этом пользователю (а в некоторых случаях и команде разработчиков, например, отправив электронное письмо). почта с трассировкой стека). Таким образом, обычно одна и та же строка / блок кода используется в каждом месте, где обрабатывается исключение.
Давайте предположим, что мы делаем простую регистрацию в каждом блоке catch для некоторого типа проверяемого исключения:
Если это общее исключение, в большой кодовой базе может быть даже несколько сотен таких блоков try-catch. Теперь давайте предположим, что нам нужно ввести обработку исключений на основе всплывающего диалогового окна вместо ведения журнала консоли или начать дополнительно отправлять электронную почту группе разработчиков.
Подождите минуту ... мы действительно собираемся отредактировать все эти несколько сотен мест в коде ?! Вы поняли мою точку зрения :-).
Решение
Чтобы решить эту проблему, мы представили концепцию обработчиков исключений (которые я далее буду называть EH) для централизации обработки исключений. Каждому классу, который должен обрабатывать исключения, экземпляр обработчика исключений внедряется нашей средой внедрения зависимостей . Таким образом, типичная схема обработки исключений теперь выглядит так:
Теперь, чтобы настроить обработку исключений, нам нужно всего лишь изменить код в одном месте (код EH).
Конечно, для более сложных случаев мы можем реализовать несколько подклассов EH и использовать функции, которые предоставляет нам наша структура DI. Изменяя нашу конфигурацию DI-инфраструктуры, мы можем легко переключать реализацию EH глобально или предоставлять конкретные реализации EH для классов с особыми потребностями обработки исключений (например, с помощью аннотации Guice @Named).
Таким образом, мы можем дифференцировать поведение обработки исключений в разработке и выпуске версии приложения (например, разработка - регистрация ошибки и остановка приложения, продолжение - регистрация ошибки с более подробной информацией и предоставление приложению возможности продолжить его выполнение) без каких-либо усилий.
Последняя вещь
И последнее, но не менее важное: может показаться, что такой же тип централизации можно получить, просто передав наши исключения «вверх», пока они не достигнут некоторого класса обработки исключений высшего уровня. Но это приводит к загромождению кода и сигнатур наших методов и приводит к проблемам обслуживания, упомянутым другими в этой теме.
источник
catch
блоков в этом реальном проекте делает что-то действительно «полезное» с исключением? 10% было бы хорошо. Обычные проблемы, которые генерируют исключения, похожи на попытку чтения конфигурации из несуществующего файла, OutOfMemoryErrors, NullPointerExceptions, ошибки целостности ограничений базы данных и т. Д., И т. Д. Вы действительно пытаетесь корректно восстановить все из них? Я тебе не верю :). Часто просто нет возможности восстановиться.Андерс говорит о подводных камнях проверенных исключений и почему он оставил их вне C # в эпизоде 97 радио Software Engineering.
источник
Моя рецензия на c2.com по-прежнему практически не изменилась по сравнению с первоначальной формой: CheckedExceptionsAreIncompatibleWithVisitorPattern
В итоге:
Шаблон посетителя и его родственники - это класс интерфейсов, в которых косвенный вызывающий и интерфейсный интерфейсы знают об исключении, но интерфейс и прямой вызывающий образуют библиотеку, которая не может знать.
Основное предположение CheckedExceptions - все объявленные исключения могут быть выброшены из любой точки, которая вызывает метод с этим объявлением. VisitorPattern показывает, что это предположение является ошибочным.
Конечным результатом проверенных исключений в подобных случаях является множество бесполезного кода, который по существу снимает ограничение проверенных исключений компилятора во время выполнения.
Что касается основной проблемы:
Моя общая идея - обработчик верхнего уровня должен интерпретировать исключение и отобразить соответствующее сообщение об ошибке. Я почти всегда вижу либо исключения ввода-вывода, исключения связи (по какой-то причине API-интерфейсы различаются), либо неустранимые ошибки задачи (программные ошибки или серьезную проблему на сервере резервного копирования), поэтому это не должно быть слишком сложно, если мы разрешаем трассировку стека для серьезного проблема с сервером.
источник
Чтобы попытаться ответить только на оставшийся без ответа вопрос:
Вопрос содержит ложные рассуждения ИМХО. Тот факт, что API сообщает вам, что он выдает, не означает, что вы будете работать с ним одинаково во всех случаях. Иными словами, исключения, которые нужно отлавливать, различаются в зависимости от контекста, в котором вы используете компонент, вызывающий исключение.
Например:
Если я пишу тестер соединений для базы данных или что-то для проверки правильности ввода пользователем XPath, то я, вероятно, захочу отловить и сообщить обо всех проверенных и непроверенных исключениях, которые выдает операция.
Однако, если я пишу обработчик, я, скорее всего, буду относиться к XPathException (проверено) так же, как и к NPE: я бы позволил ему запускаться до вершины рабочего потока, пропустил оставшуюся часть этого пакета, проблема (или отправить его в службу поддержки для диагностики) и оставить отзыв для пользователя, чтобы связаться со службой поддержки.
источник
Эта статья - лучший текст по обработке исключений в Java, который я когда-либо читал.
Он одобряет непроверенные над проверенными исключениями, но этот выбор объясняется очень подробно и основан на веских аргументах.
Я не хочу приводить здесь слишком много содержания статьи (лучше читать ее целиком), но она охватывает большинство аргументов сторонников непроверенных исключений из этой темы. Особенно этот аргумент (который кажется довольно популярным) покрыт:
Автор "Отзывы":
И главная мысль или статья:
Так что, если « никто не знал, что эта ошибка могла произойти », значит что-то не так с этим проектом. Такое исключение должно обрабатываться по крайней мере самым общим обработчиком исключений (например, тем, который обрабатывает все исключения, не обработанные более конкретными обработчиками), как предлагает автор.
Так печально, что не многие люди открывают для себя эту замечательную статью :-(. Я рекомендую всем, кто колеблется, какой подход лучше взять некоторое время и прочитать его.
источник
Проверенные исключения в своей первоначальной форме были попыткой обработки непредвиденных обстоятельств, а не сбоев. Похвальной целью было выделить определенные предсказуемые точки (невозможно подключиться, файл не найден и т. Д.) И убедиться, что разработчики справились с ними.
То, что никогда не было включено в первоначальную концепцию, заключалось в том, чтобы принудительно объявлять широкий спектр системных и неисправимых отказов. Эти сбои никогда не были правильными, чтобы быть объявленными как проверенные исключения.
Сбои обычно возможны в коде, и контейнеры EJB, web и Swing / AWT уже учитывают это, предоставляя самый внешний обработчик исключений «сбой запроса». Самая основная правильная стратегия - откатить транзакцию и вернуть ошибку.
Одним из важных моментов является то, что время выполнения и проверенные исключения функционально эквивалентны. Не существует обработки или восстановления, которые могут делать проверенные исключения, а исключения времени выполнения - нет.
Самый большой аргумент против «проверенных» исключений заключается в том, что большинство исключений нельзя исправить. Простой факт в том, что мы не владеем кодом / подсистемой, которая сломалась. Мы не видим реализацию, мы не несем за нее ответственность и не можем ее исправить.
Если наше приложение не является БД ... мы не должны пытаться исправить БД. Это нарушило бы принцип инкапсуляции .
Особенно проблематичными были области JDBC (SQLException) и RMI для EJB (RemoteException). Вместо определения исправляемых непредвиденных обстоятельств в соответствии с первоначальной концепцией «проверенных исключений» эти широко распространенные проблемы вынужденной системной надежности, которые на самом деле не поддаются устранению, должны быть широко заявлены.
Другим серьезным недостатком Java-дизайна было то, что обработка исключений должна быть правильно размещена на максимально возможном уровне «бизнес» или «запрос». Принцип здесь "бросай рано, лови поздно". Проверенные исключения мало что делают, но мешают этому.
У нас есть очевидная проблема в Java, когда требуются тысячи блоков без пробных попыток, при этом значительная часть (более 40%) неправильно кодируется. Почти ни один из них не реализует никакой подлинной обработки или надежности, но накладывает большие накладные расходы на кодирование.
Наконец, «проверенные исключения» в значительной степени несовместимы с функциональным программированием на FP.
Их настойчивое требование «обрабатывать немедленно» противоречит как передовой практике «обработки с опозданием», так и любой структуре FP, которая абстрагирует циклы / или поток управления.
Многие люди говорят об «обработке» проверенных исключений, но говорят через свои шляпы. Продолжение после сбоя с нулевыми, неполными или неверными данными, чтобы притвориться успешным, ничего не обрабатывает. Это халатность инженерии / надежности самой низкой формы.
Чистая неудача - самая основная правильная стратегия обработки исключения. Откат транзакции, регистрация ошибки и сообщение пользователю о «неудачном» ответе - разумная практика, и самое главное, предотвращение передачи неверных бизнес-данных в базу данных.
Другими стратегиями обработки исключений являются «повтор», «переподключение» или «пропуск» на уровне бизнеса, подсистемы или уровня запросов. Все это общие стратегии надежности, которые работают хорошо / лучше с исключениями времени выполнения.
Наконец, гораздо лучше потерпеть неудачу, чем работать с неверными данными. Продолжение может привести к вторичным ошибкам, далеким от первоначальной причины, и более сложным для отладки; или в конечном итоге приведет к ошибочным фиксации данных. Люди уволены за это.
Смотрите:
- http://literatejava.com/exceptions/checked-exceptions-javas-biggest-mistake/
источник
Как уже говорили, проверенные исключения не существуют в байт-коде Java. Это просто механизм компиляции, мало похожий на другие проверки синтаксиса. Я вижу , проверяемые исключения много , как я вижу , что компилятор жалуется на избыточный условный:
if(true) { a; } b;
. Это полезно, но я мог сделать это специально, поэтому позвольте мне проигнорировать ваши предупреждения.Дело в том, что вы не сможете заставить каждого программиста «делать правильные вещи», если вы применяете проверенные исключения, а все остальные теперь получают побочный ущерб, который просто ненавидит вас за правило, которое вы сделали.
Исправьте плохие программы там! Не пытайтесь исправить язык, чтобы не допустить их! Для большинства людей «делать что-то с исключением» на самом деле просто говорит пользователю об этом. Я также могу рассказать пользователю о неконтролируемом исключении, так что держите проверенные классы исключений в моем API.
источник
Проблема с проверенными исключениями заключается в том, что исключения часто присоединяются к методам интерфейса, если его использует хотя бы одна реализация этого интерфейса.
Другая проблема с проверенными исключениями заключается в том, что они, как правило, используются не по назначению. Прекрасным примером этого является в
java.sql.Connection
«Sclose()
методом. Он может выдатьSQLException
, даже если вы уже явно заявили, что закончили с подключением. Какую информацию может закрыть (), возможно, передать, что вы заботитесь?Обычно, когда я закрываю () соединение
*
, оно выглядит примерно так:Кроме того, не заставляйте меня начинать с различных методов синтаксического анализа и NumberFormatException .... TryParse .NET, который не вызывает исключений, намного проще в использовании, это больно возвращаться к Java (мы используем и Java, и C # где я работаю).
*
В качестве дополнительного комментария, Connection.close () PooledConnection даже не закрывает соединение, но вам все равно нужно перехватить SQLException, поскольку оно является проверенным исключением.источник
Программист должен знать все исключения, которые может вызвать метод, чтобы правильно его использовать. Таким образом, избиение его по голове только некоторыми исключениями не обязательно помогает небрежному программисту избежать ошибок.
Тонкая выгода перевешивается обременительными затратами (особенно в больших, менее гибких базах кода, где постоянное изменение сигнатур интерфейса нецелесообразно).
Статический анализ может быть хорошим, но действительно надежный статический анализ часто жестко требует от программиста строгой работы. Существует расчет затрат и выгод, и для проверки, которая приводит к ошибке времени компиляции, необходимо установить высокую планку. Было бы более полезно, если бы IDE взяла на себя роль сообщения о том, какие исключения может вызвать метод (в том числе и те, которые неизбежны). Хотя, возможно, это не было бы столь же надежно без принудительных объявлений исключений, большинство исключений все равно будет объявлено в документации, и надежность предупреждения IDE не так важна.
источник
Я думаю, что это отличный вопрос и совсем не спорный. Я думаю, что сторонние библиотеки должны (в общем) генерировать непроверенные исключения. Это означает, что вы можете изолировать свои зависимости от библиотеки (то есть вам не нужно ни перебрасывать их исключения, ни выбрасывать
Exception
- обычно это плохая практика). Слой Spring DAO является отличным примером этого.С другой стороны, исключения из основного Java API должны, как правило, проверяться, если они когда-либо могут быть обработаны. Возьми
FileNotFoundException
или (мой любимый)InterruptedException
. Эти условия почти всегда должны рассматриваться конкретно (т.е. ваша реакция на anInterruptedException
не совпадает с вашей реакцией на anIllegalArgumentException
). Тот факт, что ваши исключения проверены, заставляет разработчиков задуматься о том, является ли условие пригодным для обработки или нет. (Тем не менее, я редко видел, какInterruptedException
обрабатываются правильно!)Еще одна вещь - это
RuntimeException
не всегда «где разработчик что-то не так». Исключение недопустимого аргумента генерируется, когда вы пытаетесь создатьenum
использование,valueOf
иenum
этого имени нет. Это не обязательно ошибка разработчика!источник
enum
имена членов просто потому, чтоenum
вместо них они используют объекты. Таким образом, неправильное имя может быть получено извне, будь то файл импорта или что-то еще. Один из возможных способов справиться с такими именами - звонитьMyEnum#valueOf
и ловить IAE. Другой способ - использовать предварительно заполненныеMap<String, MyEnum>
, но это детали реализации.Вот один аргумент против проверенных исключений (от joelonsoftware.com):
источник
goto
ключевое слово. С помощью цикла вы можете увидеть закрывающую скобкуbreak
илиcontinue
ключевое слово или . Все они переходят к точке текущего метода. Но вы не всегда можете видетьthrow
, потому что часто это не в текущем методе, а в другом методе, который он вызывает (возможно косвенно)Хорошие доказательства того, что Checked Exception не нужны:
Я был счастлив, если бы java предоставил мне выбор, что использовать при работе с основными библиотеками, такими как I / O. Like предоставляет две копии одних и тех же классов - один обернут в RuntimeEception. Тогда мы можем сравнить, что люди будут использовать . На данный момент, тем не менее, многим людям лучше выбрать какой-нибудь фреймворк на Java или другой язык. Как Скала, JRuby, что угодно. Многие просто верят, что SUN был прав.
источник
RuntimeException
с соответствующим внутренним исключением). К сожалению, более лаконично иметь внешний методthrows
как исключение из внутреннего метода, чем включать в него исключения из внутреннего метода, когда последний способ действий будет более правильным.Одна важная вещь, которую никто не упомянул, это то, как это мешает интерфейсам и лямбда-выражениям
Допустим, вы определяете
MyAppException extends Exception
. Это исключение верхнего уровня, унаследованное всеми исключениями, выданными вашим приложением. Каждый метод объявляет,throws MyAppException
что немного неудобно, но управляемо. Обработчик исключений регистрирует исключение и как-то уведомляет пользователя.Все выглядит хорошо, пока вы не захотите реализовать какой-то интерфейс, который не ваш. Очевидно, что он не объявляет намерение выбросить
MyApException
, поэтому компилятор не позволяет вам генерировать исключение оттуда.Однако, если ваше исключение распространяется
RuntimeException
, не будет проблем с интерфейсами. Вы можете добровольно упомянуть об исключении в JavaDoc, если хотите. Но, кроме этого, он просто беззвучно пробивается сквозь что-либо, чтобы оказаться в вашем слое обработки исключений.источник
Мы видели некоторые ссылки на главного архитектора C #.
Вот альтернативная точка зрения парня из Java о том, когда использовать проверенные исключения. Он признает многие негативы, о которых упоминали другие: Эффективные исключения
источник
Я много читал об обработке исключений, даже если (большую часть времени) я не могу сказать, что я доволен или расстроен существованием проверенных исключений, это мое мнение: проверенные исключения в низкоуровневом коде (IO, работа в сети) , ОС и т. Д.) И непроверенные исключения в высокоуровневых API / уровне приложений.
Даже если между ними не так просто провести черту, я нахожу, что действительно раздражает / трудно интегрировать несколько API / библиотек под одной крышей, не оборачивая все время множество проверенных исключений, но, с другой стороны, иногда полезно / лучше быть вынужденным поймать какое-то исключение и предоставить другое, которое имеет больше смысла в текущем контексте.
Проект, над которым я работаю, берет много библиотек и интегрирует их в один API, API, который полностью основан на непроверенных исключениях. Эти платформы предоставляют высокоуровневый API, который в начале был полон проверенных исключений и имел только несколько непроверенных исключения (Initialization Exception, ConfigurationException и т. д.) и, надо сказать, был не очень дружелюбным . Большую часть времени вам приходилось ловить или перебрасывать исключения, которые вы не знаете, как обрабатывать, или вас это даже не волнует (не путать с вами следует игнорировать исключения), особенно на стороне клиента, где один клик может вызвать 10 возможных (проверенных) исключений.
Текущая версия (3-я версия) использует только непроверенные исключения и имеет глобальный обработчик исключений, который отвечает за обработку всего необработанного. API предоставляет способ регистрации обработчиков исключений, который решает, будет ли исключение считаться ошибкой (в большинстве случаев это так), что означает запись и уведомление кого-либо, или это может означать что-то другое - например, это исключение AbortException, что означает прерывание текущего потока выполнения и не регистрировать ошибки, потому что это нежелательно. Конечно, для того, чтобы отработать все пользовательские потоки, необходимо обработать метод run () с помощью try {...} catch (all).
public void run () {
}
В этом нет необходимости, если вы используете WorkerService для планирования заданий (Runnable, Callable, Worker), который обрабатывает все за вас.
Конечно, это только мое мнение, и оно может быть неправильным, но это выглядит как хороший подход ко мне. После выхода проекта я увижу, будет ли то, что я считаю, хорошим для меня, хорошим и для других ... :)
источник