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