Русский
Русский
English
Статистика
Реклама

Еще раз про try и Try

Исключения, проверяемые и нет

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

Например, есть функция по считыванию числа из файла (или не числа, не важно):

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, когда непроверяемые исключения говорят об ошибке в коде (а не например в файловой системе).

Поэтому в изначальной реализации функции у нас было два скрытых случая ошибки:

  1. FileNotFoundException если файла нет, что вероятно логическая ошибка или ожидаемое поведение

  2. Другие 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]] может выглядеть непривычно, но теперь он явно говорит, что результатом могут быть три отдельных случая:

  1. None нет файла

  2. Some(Success(string)) собственно строка из файла

  3. Some(Failure(exception)) ошибка считывания файла, в случае если он существует

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

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

Источник: habr.com
К списку статей
Опубликовано: 31.01.2021 22:19:51
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Программирование

Java

Scala

Функциональное программирование

Исключения

Exceptions

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru