“Sneaky Throws” in Java – Java中的 “偷偷摸摸的扔”

最后修改: 2017年 9月 4日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In Java, the sneaky throw concept allows us to throw any checked exception without defining it explicitly in the method signature. This allows the omission of the throws declaration, effectively imitating the characteristics of a runtime exception.

在Java中,sneaky throw概念允许我们抛出任何检查过的异常,而不需要在方法签名中明确定义它。这允许省略throws声明,有效地模仿了运行时异常的特征。

In this article, we’ll see how this is done in practice, by looking at some code examples.

在这篇文章中,我们将通过看一些代码例子来看看在实践中是如何做到这一点的。

2. About Sneaky Throws

2.关于偷偷摸摸的投掷

Checked exceptions are part of Java, not the JVM. In the bytecode, we can throw any exception from anywhere, without restrictions.

检查异常是Java的一部分,而不是JVM的一部分。在字节码中,我们可以从任何地方抛出任何异常,没有限制。

Java 8 brought a new type inference rule that states that a throws T is inferred as RuntimeException whenever allowed. This gives the ability to implement sneaky throws without the helper method.

Java 8带来了一个新的类型推理规则,规定只要允许,throws T就会被推断为RuntimeException。这使我们有能力在没有辅助方法的情况下实现偷偷摸摸的抛出。

A problem with sneaky throws is that you probably want to catch the exceptions eventually, but the Java compiler doesn’t allow you to catch sneakily thrown checked exceptions using exception handler for their particular exception type.

一个关于偷偷抛出的问题是,你可能想最终捕捉到这些异常,但Java编译器不允许你使用异常处理程序来捕捉偷偷抛出的检查异常,因为它们的特定异常类型。

3. Sneaky Throws in Action

3.偷偷摸摸的投掷行动

As we already mentioned, the compiler and the Jave Runtime can see different things:

正如我们已经提到的,编译器和Jave Runtime可以看到不同的东西。

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

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

The compiler sees the signature with the throws T inferred to a RuntimeException type, so it allows the unchecked exception to propagate. The Java Runtime doesn’t see any type in the throws as all throws are the same a simple throw e.

编译器看到签名中的throws T被推断为RuntimeException类型,所以它允许未检查的异常传播。Java Runtime在throws中没有看到任何类型,因为所有的throws都是一样的,都是一个简单的throw e

This quick test demonstrates the scenario:

这个快速测试演示了这种情况。

@Test
public void throwSneakyIOException_IOExceptionShouldBeThrown() {
    assertThatThrownBy(() -> throwSneakyIOException())
      .isInstanceOf(IOException.class)
      .hasMessage("sneaky")
      .hasStackTraceContaining("SneakyThrowsExamples.throwSneakyIOException");
}

Furthermore, it’s possible to throw a checked exception using bytecode manipulation, or Thread.stop(Throwable), but it’s messy and not recommended.

此外,使用字节码操作或Thread.stop(Throwable)抛出一个检查过的异常也是可能的,但这很混乱,不推荐。

4. Using Lombok Annotations

4.使用Lombok注释

The @SneakyThrows annotation from Lombok allows you to throw checked exceptions without using the throws declaration. This comes in handy when you need to raise an exception from a method within very restrictive interfaces like Runnable.

来自Lombok@SneakyThrowsannotation允许您抛出经过检查的异常,而无需使用throws声明。当你需要从像Runnable.这样的限制性接口中的方法引发一个异常时,这就很方便了。

Say we throw an exception from within a Runnable; it will only be passed to the Thread’s unhandled exception handler.

假设我们从Runnable中抛出一个异常;它只会被传递给Threads未处理的异常处理器。

This code will throw the Exception instance, so there is no need for you to wrap it in a RuntimeException:

这段代码将抛出Exception 实例,所以你没有必要将其包裹在RuntimeException:中。

@SneakyThrows
public static void throwSneakyIOExceptionUsingLombok() {
    throw new IOException("lombok sneaky");
}

A drawback with this code is that you cannot catch a checked exception that is not declared. For instance, if we try to catch the IOException sneakily thrown by the method above, we would get a compilation error.

这段代码的一个缺点是,你无法捕捉到一个没有被声明的检查异常。例如,如果我们试图捕捉上述方法偷偷抛出的IOException,我们会得到一个编译错误。

Now, let’s call throwSneakyIOExceptionUsingLombok and expect Lombok to throw the IOException:

现在,让我们调用throwSneakyIOExceptionUsingLombok并期望Lombok抛出IOException。

@Test
public void throwSneakyIOExceptionUsingLombok_IOExceptionShouldBeThrown() {
    assertThatThrownBy(() -> throwSneakyIOExceptionUsingLombok())
      .isInstanceOf(IOException.class)
      .hasMessage("lombok sneaky")
      .hasStackTraceContaining("SneakyThrowsExamples.throwSneakyIOExceptionUsingLombok");
}

5. Conclusion

5.结论

As we have seen in this article, we can trick the Java compiler to treat checked exceptions as unchecked.

正如我们在本文中所看到的,我们可以欺骗Java编译器,将有检查的异常视为无检查的。

As always, the code is available over on GitHub.

像往常一样,代码可在GitHub上获得