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

“Подлые броски” на Java

Узнайте о концепции sneakythrow, которая позволяет создавать любое проверенное исключение, не определяя его явно в сигнатуре метода.

Автор оригинала: baeldung.

1. Обзор

В Java концепция sn eaky throw позволяет нам выбрасывать любое проверенное исключение, не определяя его явно в сигнатуре метода. Это позволяет опустить объявление throws , эффективно имитируя характеристики исключения во время выполнения.

В этой статье мы увидим, как это делается на практике, обратившись к некоторым примерам кода.

2. О Подлых Бросках

Проверенные исключения являются частью Java, а не JVM. В байт-коде мы можем выбросить любое исключение из любого места без ограничений.

В Java 8 появилось новое правило вывода типа, которое гласит, что выбрасывает T выводится как RuntimeException всякий раз, когда это разрешено. Это дает возможность реализовать скрытные броски без вспомогательного метода.

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

3. Хитрые броски в действии

Как мы уже упоминали, компилятор и среда выполнения Java могут видеть разные вещи:

public static  void sneakyThrow(Throwable e) throws E {
    throw (E) e;
}

private static void throwsSneakyIOException() {
    sneakyThrow(new IOException("sneaky"));
}

Компилятор видит подпись с throws T , выведенную на RuntimeException type , поэтому он позволяет распространяться непроверенному исключению. Среда выполнения Java не видит никакого типа в бросках, так как все броски одинаковы: простой бросок e .

Этот быстрый тест демонстрирует сценарий:

@Test
public void whenCallSneakyMethod_thenThrowSneakyException() {
    try {
        SneakyThrows.throwsSneakyIOException();
    } catch (Exception ex) {
        assertEquals("sneaky", ex.getMessage().toString());
    }
}

Можно создать проверенное исключение с помощью манипуляции байт-кодом или Thread.stop(Throwable) , но это грязно и не рекомендуется.

4. Использование аннотаций Ломбока

Аннотация @SneakyThrows из Lombok позволяет создавать проверенные исключения без использования объявления throws . Это удобно, когда вам нужно вызвать исключение из метода в очень ограничительных интерфейсах, таких как Runnable.

Допустим, мы выбрасываем исключение из Runnable ; оно будет передано только в Thread’ s unhandledexceptionhandler.

Этот код вызовет экземпляр Exception , поэтому вам не нужно заключать его в исключение RuntimeException:

public class SneakyRunnable implements Runnable {
    @SneakyThrows(InterruptedException.class)
    public void run() {
        throw new InterruptedException();
    }
}

Недостатком этого кода является то, что вы не можете поймать проверенное исключение, которое не объявлено; поэтому оно не будет компилироваться .

Вот правильная форма для создания скрытого исключения:

@SneakyThrows
public void run() {
    try {
        throw new InterruptedException();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

И вот тест на такое поведение:

@Test
public void whenCallSneakyRunnableMethod_thenThrowException() {
    try {
        new SneakyRunnable().run();
    } catch (Exception e) {
        assertEquals(InterruptedException.class, e.getStackTrace());
    }
}

5. Заключение

Как мы видели в этой статье, компилятор Java может быть обманут, чтобы рассматривать проверенные исключения как непроверенные.

Как всегда, код доступен на GitHub .