Проверенные исключения являются лишь небольшим примером более общего подхода к тому, что Эрик Мейер называет тип честность . То есть процедуры, методы, функции не должны совпадать с их типами. Если вы видите сигнатуру типа, вы должны доверять ее типу.
Это не так для Java сегодня (особенно если вы представляете себе Java - без проверенных исключений).
Если у вас есть такая подпись в Java:
Foo bar(Baz)
он говорит: "Я беру Baz
в качестве ввода и выдаю Foo
в качестве вывода". Но это ложь.
На самом деле, bar
принимает либо a Baz
или null
в качестве ввода. Он также принимает полное глобальное состояние, состояние класса и состояние экземпляра в качестве входных данных, а также весь юниверс (например, через файловый ввод-вывод, сетевой ввод-вывод, ввод-вывод базы данных и т. Д.). ). И он не выдает Baz
в качестве вывода: он выдает или a Foo
или null
или исключение или Bottom
(т.е. вообще ничего). Плюс его выходные данные также включают в себя все глобальное состояние, все состояние класса, все состояние экземпляра и в действительности также все состояние вселенной.
bar
s фактический тип:
(IO<Foo | null | Exception> | Bottom) bar(IO<Baz | null>)
или что-то в этом роде.
Это нужно исправить, и Проверенные Исключения являются (очень маленькой) частью этого. Лично я считаю, что другие части более важны, и разработчики Java должны были сосредоточиться на исправлении этих , а не исключений (тем более, что исключения в любом случае являются лишь побочными эффектами, и поэтому вы фактически в значительной степени автоматически исправляете исключения для бесплатно при устранении побочных эффектов).
В любом случае, именно поэтому я считаю, что общая идея за Checked Exceptions - хорошая вещь & trade;, даже если конкретная реализация в Java может быть немного громоздкой.
Как исправить отмеченные исключения во многом зависит от того, что именно вы думаете на самом деле неправильно с ними.
Некоторые люди считают, что проблема с Проверяемыми Исключениями заключается в том, что когда вы меняете внутреннюю реализацию вашего метода на использование другого вспомогательного метода, чем он делал раньше, который генерирует другой набор исключений, чем старый, вам нужно либо явно обрабатывать эти исключения или объявлять их, тем самым нарушая работу всех ваших клиентов. Теперь, если вы считаете, что это неправильно с Checked Exceptions, то есть только один способ их исправить: не создавать их в первую очередь. Изменение исключений, которые вы генерируете , является критическим изменением в вашем контракте API, а нарушение изменений в вашем контракте API должно нарушить код клиента. (Или, точнее: вам не следует вносить критические изменения в ваш контракт API, чтобы не нарушать код клиента.)
Я полагаю, что основная проблема с Проверяемыми Исключениями, реализованными в Java, заключается в том, что они нарушают одну из основных функций исключений: нелокальную обработку ошибок. Ошибка происходит здесь и обрабатывается там, и эти два - единственные, кто должен знать об этом. Если здесь может произойти ошибка другого типа, то место only , которое должно знать об этой новой ошибке, и единственное место, где нужно change , - это там обработчик ошибок.
С Checked Exceptions, как реализовано в Java, каждый фрагмент кода между также должен быть изменен.
Одним из предложений по решению этой проблемы являются Привязанные объявления об исключениях . (Улучшено в Модульных объявленных исключениях .)
Идея якорных объявлений исключений заключается в основном в использовании делегирования для объявлений исключений так же, как вы используете делегирование в теле метода, что, в конце концов, и создает проблему.
Допустим, у вас есть метод чтения файлов, который делегирует другой метод:
String fileReader(String filename) {
return this.fileHelper.read(filename);
}
Теперь вы заходите в JavaDoc для FileHelper#read
и вырезаете и вставляете список исключений в свой метод:
String fileReader(String filename) throws IOException, CustomFileReaderEx
Теперь автор FileHelper#read
решает, что он использует другую стратегию реализации.Теперь он удостоверится, что чтение файла фактически никогда не может завершиться неудачей, сначала убедившись, что файл существует, может быть открыт и имеет правильный формат.Так что, естественно, набор исключений меняется.Больше невозможно получить IOException
или CustomFileReaderEx
.Вместо этого вы можете получить InvalidFilenameEx
или CorruptDataEx
.Итак, вы снова вырезаете и вставляете:
String fileReader(String filename) throws InvalidFilenameEx, CorruptDataEx
Не только вам нужно было внести это изменение, но и всем, кто звонит fileReader
(и всем, кто звонит им, и всем, кто звонит ими ...) также.Это безумие!Причина, по которой вы в первую очередь делегировали вызов fileHelper
, заключалась в том, что вам не нужно беспокоиться об этих деталях.
Итак, идея якорных объявлений исключений состоит в том, чтобы использовать это делегирование для исключения.Сами декларации.Вместо того чтобы говорить, какие точные исключения вы выбрасываете, вы просто обвиняете кого-то другого.«Он сделал это!»:
String fileReader(String filename) throws like this.fileHelper.read
А ваши клиенты просто говорят:
Foo whatever() throws like fileReader
Таким образом, когда FileHelper
меняет свои исключения, тогда только код, который нужно изменить, - это код обработки исключений самого верхнего уровня, как я описал выше для случая unchecked .
Конечно, существуют ограничения.Например, чтобы не нарушать инкапсуляцию, вы можете использовать только те идентификаторы в предложении throws like
, которые доступны всем вашим клиентам.Если в этом случае fileHelper
является полем private
, вы не можете его использовать.Тебе нужен другой путь.Например, если класс FileHelper
равен public
(или это частный пакет и все ваши клиенты живут в одном пакете), вы можете вместо этого сказать
String fileReader(String filename) throws like FileHelper.read
Есть и другие ограничения, перечисленные в документе.(Один из них поднят в документе «Модульные объявления привязанных исключений».)
В любом случае, это один способ решить некоторые проблемы с проверенными исключениями.Тем не менее, Проверенные исключения существуют уже почти 40 лет, и мы до сих пор не выяснили их, так что это очевидно трудная проблема.