邪恶的Java技巧使JVM忘记检查异常

长期以来一直批评Java中的编译器检查异常机制。 无论您是爱还是恨,都可以肯定一件事:在某些情况下,您不想与他们打交道。 Java中的解决方案是将一个检查过的异常包装在new RuntimeException(e)但这可以提供较长的堆栈跟踪,而无需添加有用的信息。 有时,我们只想告诉编译器冷静。

事实证明,通过对Java泛型的类型擦除错误功能的某些不当使用,这是有可能的。 看到这一点对于理解Java的内部运作方式具有指导意义。 我们走吧!

这是我们想要的:

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()方法将删除异常的检查。 运行它时,我们得到以下堆栈跟踪:

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)

! 在main方法中引发的异常是NoSuchFileException ,它是IOException的子类–已检查的异常! 怎么可能? 为什么程序中的任何方法都不必声明throws IOException

这是窍门:

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)这在 Lombok项目中 也实现为@SneakyThrows 。 如果您使用的是Lombok,则在任何情况下都不应重新实现此博客中的技巧。 请改用@SneakyThrows

翻译自: https://www.javacodegeeks.com/2018/05/a-wicked-java-trick-to-make-the-jvm-forget-to-check-exceptions.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值