Я долгое время критиковал механизм проверенных компилятором исключений в Java. Любите ли вы их или ненавидите тогда, одно несомненно: есть ситуации, когда вы не хотите иметь с ними дело. Решение в Java — new RuntimeException(e)
проверенное исключение в new RuntimeException(e)
но это дает длинные трассировки стека без добавления полезной информации. Иногда мы просто хотим сказать компилятору успокоиться.
Как выясняется, это возможно из-за неосторожного злоупотребления ошибкой в стирании типов Java-дженериков. Увидеть это в действии полезно для понимания внутренней работы Java. Поехали!
Вот что мы хотим:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
public static void main(String[] args) { businessLogic(); } private static void businessLogic() { List<String> configuration = readConfigurationFile(); System.out.println(configuration.get( 0 )); } private static List<String> readConfigurationFile() { try { return Files.readAllLines(Paths.get( "non" , "existing" , "file" )); } catch (IOException e) { throw softenException(e); } } |
Обратите внимание, что businessLogic()
ни перехватывает IOException
ни объявляет, что throws IOException
. Вместо этого метод softenException()
удаляет проверенную исключительность. Когда мы запускаем его, мы получаем следующую трассировку стека:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
Exception in thread "main" java.nio. file .NoSuchFileException: non\existing\ file at sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:79) at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:97) at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:102) at sun.nio.fs.WindowsFileSystemProvider.newByteChannel(WindowsFileSystemProvider.java:230) at java.nio. file .Files.newByteChannel(Files.java:361) at java.nio. file .Files.newByteChannel(Files.java:407) at java.nio. file .spi.FileSystemProvider.newInputStream(FileSystemProvider.java:384) at java.nio. file .Files.newInputStream(Files.java:152) at java.nio. file .Files.newBufferedReader(Files.java:2784) at java.nio. file .Files.readAllLines(Files.java:3202) at java.nio. file .Files.readAllLines(Files.java:3242) at insanejava.SoftenExceptionsDemo.readConfigurationFile(SoftenExceptionsDemo.java:21) at insanejava.SoftenExceptionsDemo.businessLogic(SoftenExceptionsDemo.java:15) at insanejava.SoftenExceptionsDemo.main(SoftenExceptionsDemo.java:11) |
Ха! Исключение, которое NoSuchFileException
методе main, представляет собой NoSuchFileException
, который является подклассом IOException
— проверенное исключение! Как это может быть? Почему ни один из методов в программе не должен объявлять throws IOException
?
Вот хитрость:
1
2
3
4
5
6
7
|
private static RuntimeException softenException(Exception e) { return checkednessRemover(e); } private static <T extends Exception> T checkednessRemover(Exception e) throws T { throw (T) e; } |
Метод checkednessRemover
использует трюк, который раскрывает несколько вещей о внутренней работе Java. Во-первых, аргумент универсального типа T связан с RuntimeException
для выполнения контракта softenException
. Это означает, что выражение throws T
становится throws RuntimeException
, которое компилятор интерпретирует, как если бы не было выброшенных исключений.
Но утверждение throw (T)e;
Теоретически следует оценить, чтобы throw (RuntimeException)e;
, Поскольку e является NoSuchFileException
, можно ожидать, что этот оператор приведет к ClassCastException
. Но как работают дженерики в Java, информация о типе удаляется компилятором. Поэтому вместо этого байт-код читается как throw (Exception)e;
это нормально.
Таким образом, этот странный трюк показывает, что компилятор Java удаляет общую информацию из скомпилированного кода и что проверенные исключения являются исключительно функцией компилятора. Нет проверок во время выполнения проверенных исключений.
Я бы порекомендовал использовать этот трюк в рабочем коде? Я не знаю. Это довольно странно и, возможно, не очень полезно, но я использую его сам, когда чувствую себя злым. Надеюсь, что если вы узнаете что-то еще, вы получили некоторое представление о внутренней работе Java.
Отказ от ответственности : (1) Я читал об этом трюке в другом месте, но я не могу найти источник больше. Я думал, что это была отличная новостная рассылка Heinz Kabutz для Java-специалиста , но я не могу найти источник. (2) Это также реализовано в Project Lombok как
@SneakyThrows
. Если вы используете Lombok, вы ни при каких обстоятельствах не должны повторно использовать трюк из этого блога.@SneakyThrows
этого используйте@SneakyThrows
.
Опубликовано на Java Code Geeks с разрешения Йоханнеса Бродвола, партнера нашей программы JCG. См. Оригинальную статью здесь: Злая уловка Java, чтобы заставить JVM забыть проверить исключения
Мнения, высказанные участниками Java Code Geeks, являются их собственными. |