1. Обзор
В Java концепция sn eaky throw позволяет нам выбрасывать любое проверенное исключение, не определяя его явно в сигнатуре метода. Это позволяет опустить объявление throws , эффективно имитируя характеристики исключения во время выполнения.
В этой статье мы увидим, как это делается на практике, обратившись к некоторым примерам кода.
2. О Подлых Бросках
Проверенные исключения являются частью Java, а не JVM. В байт-коде мы можем выбросить любое исключение из любого места без ограничений.
В Java 8 появилось новое правило вывода типа, которое гласит, что выбрасывает T выводится как RuntimeException всякий раз, когда это разрешено. Это дает возможность реализовать скрытные броски без вспомогательного метода.
Проблема с sneakythrows заключается в том, что вы, вероятно, захотите в конечном итоге перехватить исключения, но компилятор Java не позволяет вам перехватывать украдкой брошенные проверенные исключения, используя обработчик исключений для их конкретного типа исключений.
3. Хитрые броски в действии
Как мы уже упоминали, компилятор и среда выполнения Java могут видеть разные вещи:
public staticvoid 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 .