[Программирование, Java, Scala, Функциональное программирование] Еще раз про try и Try
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Исключения, проверяемые и нетЕсли кратко, то исключения нужны для отделения положительного сценария (когда все идет как надо) от отрицательного (когда случается ошибка и положительный сценарий прерывается). Это полезно, поскольку очень часто информации для обработки ошибки в коде мало и требуется передать информацию о случившемся выше.Например, есть функция по считыванию числа из файла (или не числа, не важно):
String readStoredData(String id) throws FileNotFoundException, IOException {
File file = new File(storage, id + ".dat");
try (BufferedReader in = new BufferedReader(new FileReader(file))) {
return in.readLine();
}
}
Как видно, тут нет кода, решающего что делать в случае ошибки. Да и не ясно что делать – завершить программу, вернуть "", null или еще что-то? Поэтому исключения объявлены в throws и будут обработаны где-то на вызывающей стороне:
int initCounter(String name) throws IOException, NumberFormatException {
try {
return Integer.parseInt(readStoredData(name));
} catch (FileNotFoundException e) {
return 0;
}
}
Исключения в Java делятся на проверяемые (checked) и непроверяемые (unchecked). В данном случае IOException проверяемое – вы обязаны объявить его в throws и потом где-то обработать, компилятор это проверит. NumberFormatException же непроверяемое – его обработка остается на совести программиста и компилятор вас контролировать не станет.Есть еще третий тип исключений – фатальные ошибки (Error), но их обычно нет смысла обрабатывать, поэтому вас они не должны заботить.Задумка тут состоит в том, что проверяемых исключений нельзя избежать – как ни старайся, но файловая система может подвести и чтение файла закончится ошибкой.С этим подходом есть несколько проблем:
- функциональное программирование в лице функций высших порядков плохо совместимо с проверяемыми исключениями;
- непроверяемые исключения обычно теряются и обрабатывать их забывают пока тесты (или того хуже – клиенты) не обнаружат ошибку.
Из-за первой проблемы проверяемые исключения медленно вытесняются из языка, оборачиваясь непровеяемыми. С другой стороны обостряется вторая проблема и исключения легко теряются.А что там в Scala?Как пример другого подхода возьмем Scala: язык поддерживает так же и исключения (правда все они непроверяемые), но рекомендует возвращать исключения в виде результата используя алгебраические типы данных.Возьмем к примеру Try[T] – это тип, который содержит либо значение, либо исключение. Перепишем наш код на Scala:
def readStoredData(id: String): Try[String] =
Try {
val file = new File(storage, s"$id.dat")
val source = Source.fromFile(file)
try source.getLines().next()
finally source.close()
}
def initCounter(name: String): Try[Int] = {
readStoredData(name)
.map(_.toInt)
.recover {
case _: FileNotFoundException => 0
}
}
Выглядит вполне похоже, разница в том, что тип результата функции readStoredData уже не String, а Try[String] – работая с функцией вы не забудете о возможных исключениях. В этом смысле Try похож на проверяемые исключения в Java – компилятор напомнит вам об исключении, но без проблем с лямбдами. С другой стороны недостатки тоже есть:
- вы не знаете какие конкретно виды исключений там могут быть (тут можно использовать Either[Error, T], но это тоже не очень удобно);
- в целом happy-path требует больше синтаксических ритуалов, чем исключения (Try/get или for/map/flatMap);
- люди из Java мира часто по-ошибке просто игнорируют результат вызова метода, неявно игнорируя исключения (люди из Java мира потому что такое случается в императивном коде, функциональный таким обычно не грешит).
В целом такой подход хорошо расширяется на другие эффекты (в данном случае Try[String] означает строку с эффектом – возможностью содержать ошибку вместо значения). Примерами могут быть Option[T] – потенциальное отсутствие значения, Future[T] – асинхронное вычисление значения и т.п.Исключения и ошибкиВозвращаясь к исходной проблеме стоит заметить, что если исключения можно избежать – это стоит сделать. Собственно именно исходя из этой логики были введены проверяемые/непроверяемые типы исключений в Java, когда непроверяемые исключения говорят об ошибке в коде (а не например в файловой системе).Поэтому в изначальной реализации функции у нас было два скрытых случая ошибки:
- FileNotFoundException если файла нет, что вероятно логическая ошибка или ожидаемое поведение
- Другие IOException если файл прочитать не удалось – настоящие ошибки среды
При наличии нужного инструментария в языке первый случай можно вообще не выражать в виде исключения:
def readStoredData(id: String): Option[Try[String]] = {
val file = new File(storage, s"$id.dat")
if (file.exists()) Some(
Try {
val source = Source.fromFile(file)
try source.getLines().next()
finally source.close()
}
)
else None
}
Тип результата Option[Try[String]] может выглядеть непривычно, но теперь он явно говорит, что результатом могут быть три отдельных случая:
- None – нет файла
- Some(Success(string)) – собственно строка из файла
- Some(Failure(exception)) – ошибка считывания файла, в случае если он существует
Теперь Try содержит только настоящие ошибки среды. В Java в таких случаях часто используются специальные значения, например null. Но если это поведение не выражено в типе его легко пропустить.Обилие типов создает больше визуального шума и часто требует более сложного кода при работе с несколькими эффектами одновременно. Но за это предоставляет самодокументируемый код и дает возможность компилятору найти многие ошибки.
===========
Источник:
habr.com
===========
Похожие новости:
- [C++, Программирование микроконтроллеров] Попытка использовать современный C++ и паттерны проектирования для программирования микроконтроллеров
- [JavaScript, API, ReactJS] Делаем страницу на React с базой сотрудников при помощи Airtable и Quarkly
- [Ненормальное программирование, Программирование, Java, API, C#] Вы всё ещё ловите исключения? Тогда мы к вам
- [PHP, Программирование, *nix, Отладка, Laravel] Настройка Xdebug3 для Laravel-приложения в Docker
- [Занимательные задачки, Python, Программирование, Математика] L-системы и что они себе позволяют
- [Функциональное программирование, Kotlin] Функциональный Kotlin. Во имя добра, радуги и всего такого
- [Open source, Java] Конфигурация Java систем — как убрать боль
- [Ненормальное программирование, JavaScript, HTML, Браузеры, DIY или Сделай сам] Как я создаю приложения для браузера прямо в браузере
- [Python, Программирование, Учебный процесс в IT] 10 удивительно полезных базовых функций Python (перевод)
- [Java, API] Как трассировка запросов сломала API
Теги для поиска: #_programmirovanie (Программирование), #_java, #_scala, #_funktsionalnoe_programmirovanie (Функциональное программирование), #_iskljuchenija (исключения), #_scala, #_java, #_exceptions, #_programmirovanie (
Программирование
), #_java, #_scala, #_funktsionalnoe_programmirovanie (
Функциональное программирование
)
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 25-Ноя 15:14
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Исключения, проверяемые и нетЕсли кратко, то исключения нужны для отделения положительного сценария (когда все идет как надо) от отрицательного (когда случается ошибка и положительный сценарий прерывается). Это полезно, поскольку очень часто информации для обработки ошибки в коде мало и требуется передать информацию о случившемся выше.Например, есть функция по считыванию числа из файла (или не числа, не важно): String readStoredData(String id) throws FileNotFoundException, IOException {
File file = new File(storage, id + ".dat"); try (BufferedReader in = new BufferedReader(new FileReader(file))) { return in.readLine(); } } int initCounter(String name) throws IOException, NumberFormatException {
try { return Integer.parseInt(readStoredData(name)); } catch (FileNotFoundException e) { return 0; } }
def readStoredData(id: String): Try[String] =
Try { val file = new File(storage, s"$id.dat") val source = Source.fromFile(file) try source.getLines().next() finally source.close() } def initCounter(name: String): Try[Int] = { readStoredData(name) .map(_.toInt) .recover { case _: FileNotFoundException => 0 } }
def readStoredData(id: String): Option[Try[String]] = {
val file = new File(storage, s"$id.dat") if (file.exists()) Some( Try { val source = Source.fromFile(file) try source.getLines().next() finally source.close() } ) else None }
=========== Источник: habr.com =========== Похожие новости:
Программирование ), #_java, #_scala, #_funktsionalnoe_programmirovanie ( Функциональное программирование ) |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 25-Ноя 15:14
Часовой пояс: UTC + 5