Рубрики
Без рубрики

Скрыть проверенные исключения с помощью SneakyThrows

Виктор Рентеа – чемпион Java и независимый тренер с огромным опытом работы по таким темам, как Cle… Помеченный исключениями, java, чистый код, ломбок.

Виктор Рентеа – Чемпион Java и Независимый тренер с огромным опытом работы по таким темам, как Чистый код, Шаблоны проектирования, Модульное тестирование. Узнайте больше о виктор rentea.ro .

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

Что вы делаете, когда сталкиваетесь с проверенным исключением? Распространение проверенных исключений через ваш код приводит к утечке деталей реализации, раздражает и даже опасно. Вот почему в подавляющем большинстве случаев вы должны перехватить его как исключение во время выполнения и позволить ему “молча” завершить ваш текущий вариант использования.

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

public static Date parseDate(String dateStr) {
    try {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        return format.parse(dateStr);
    } catch (ParseException e) {
        throw new RuntimeException(e); // this :(
    }
}

Если вам надоело выполнять этот перехват вручную, вот аннотация @SneakyThrows , исходящая из волшебного царства Project Lombok .

Давайте просто прокомментируем наш метод с его помощью и просто удалим предложение catch :

@SneakyThrows
public static Date parseDate(String dateStr) {
    SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
    return format.parse(dateStr);
}

Это даст указание процессору аннотаций Lombok взломать байт-код, сгенерированный компилятором javac, что позволит скомпилировать приведенный выше код, хотя для catch нет ни catch , ни throws предложения для//ParseException//.

Предупреждение : используемая вами Java IDE должна быть взломана (см. здесь как).

Зная, что Lombok может изменять ваш код по мере его компиляции , можно было бы ожидать, что текущее тело нашей функции будет окружено

try { ... } catch (Exception e) { throw new RuntimeException(e); }

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

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

public static Date parseDate(String dateStr) {
    try {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        return format.parse(dateStr);
    } catch (Throwable var6) {
        throw var6;
    }
}

Действительно, Ломбок действительно добавил блок try-catch вокруг тела моей функции, но он поймал Бросаемый и бросил его, вообще не обернув!

Что-то не так!

Этот Throwable должен был быть объявлен в предложении throws функции!

Если вы скопируете и вставите код в файл .java, вы быстро увидите, что этот код не компилируется!! Но как он был составлен в первую очередь?!

Чтобы понять, как это вообще возможно, вы должны усвоить, что различие между проверяемыми исключениями и исключениями во время выполнения обеспечивается только компилятором Java (javac). Среда выполнения Java (JVM) НЕ заботится о том, какое исключение вы создаете – она распространяет любое исключение вниз по стеку вызовов таким же образом. Таким образом, процессор Lombok обманом заставил javac создать байт-код, представляющий код, который на самом деле не поддавался компиляции.

Паника!

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

try { 
    parseDate("2020-01-01"); 
} catch (ParseException e) {...}

Но это не компилируется, потому что ничто в блоке try не выдает Исключение ParseException . И javac отвергнет это. Убедитесь сами: совершить

Так что же это значит? Это означает, что исключения, скрытые с помощью @SneakyThrows , не должны быть перехвачены снова по отдельности. Вместо этого общий catch (исключение) не ~ catch (RuntimeException) ~ должен быть где-то в стеке вызовов, чтобы перехватить невидимое проверяемое исключение . Например, если вы обрабатываете исключения конечной точки Spring REST с помощью @RestControllerAdvice , убедитесь, что вы объявили для обработки Exception.class . Более подробная информация в следующей статье .

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

Ладно, ладно… Но почему?!

Потому что Java – это старый язык. 25 лет – это долгий срок, чтобы нести какой-то багаж. Итак, сегодня Ломбок эффективно взламывает язык, чтобы заставить нас писать меньше и более целенаправленный код.

Вывод

  • Используйте Lombok @SneakyThrows для фатальных исключений, которые вы не собираетесь выборочно перехватывать.
  • В противном случае оберните проверенные исключения в исключения во время выполнения, которые вы создаете вместо этого.

Отказ от ответственности : Я специально выбрал синтаксический анализ даты, чтобы указать на типичное неустранимое исключение. Вам определенно следует использовать новый Java 8 LocalDate/LocalDateTime API, который (сюрприз!) Больше не выдает никаких проверенных исключений, кроме исключений во время выполнения.

Оригинал: “https://dev.to/victorrentea/hide-checked-exceptions-with-sneakythrows-i3h”