Seb*_*ber 22 java scjp lombok ocpjp
有人可以解释这段代码吗?
public class SneakyThrow {
public static void sneakyThrow(Throwable ex) {
SneakyThrow.<RuntimeException>sneakyThrowInner(ex);
}
private static <T extends Throwable> T sneakyThrowInner(Throwable ex) throws T {
throw (T) ex;
}
public static void main(String[] args) {
SneakyThrow.sneakyThrow(new Exception());
}
}
Run Code Online (Sandbox Code Playgroud)
它可能看起来很奇怪,但这不会产生强制转换异常,并且允许抛出已检查的异常而不必在签名中声明它,或者将其包装在未经检查的异常中.
请注意,两者都没有sneakyThrow(...)或主要声明任何已检查的异常,但输出为:
Exception in thread "main" java.lang.Exception
at com.xxx.SneakyThrow.main(SneakyThrow.java:20)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Run Code Online (Sandbox Code Playgroud)
本hack在龙目岛的使用,注释@SneakyThrow,允许抛出checked异常没有声明.
我知道它与类型擦除有关,但我不确定理解黑客的每个部分.
编辑:
我知道我们可以插入一个Integera List<String>和那个checked/unchecked异常区别是编译时功能.
从非泛型类型List转换为类似于List<XXX>编译器的泛型类型时会产生警告.但是直接转换为泛型类型并不像(T) ex上面的代码那样常见.
如果你愿意,对我来说似乎很奇怪的部分是我理解JVM中的内容List<Dog>并且List<Cat>看起来一样,但上面的代码似乎意味着我们最终还可以将类型Cat的值赋给Dog类型的变量或者类似的东西.
Jon*_*eet 18
如果你用它编译它-Xlint会得到一个警告:
c:\Users\Jon\Test>javac -Xlint SneakyThrow.java
SneakyThrow.java:9: warning: [unchecked] unchecked cast
throw (T) ex;
^
required: T
found: Throwable
where T is a type-variable:
T extends Throwable declared in method <T>sneakyThrowInner(Throwable)
1 warning
Run Code Online (Sandbox Code Playgroud)
这基本上是说" 在执行时没有真正检查此强制转换"(由于类型擦除) - 所以编译器不情愿地假设你正在做正确的事情,知道它实际上不会被检查.
现在它只是编译器关心已检查和未检查的异常 - 它根本不是JVM的一部分.所以,一旦你通过编译器,你就可以免费回家了.
我强烈建议你尽量避免这样做.
在许多情况下,当您使用泛型时会进行"真实"检查,因为某些东西使用了所需的类型 - 但情况并非总是如此.例如:
List<String> strings = new ArrayList<String>();
List raw = strings;
raw.add(new Object()); // Haha! I've put a non-String in a List<String>!
Object x = strings.get(0); // This doesn't need a cast, so no exception...
Run Code Online (Sandbox Code Playgroud)
他上面的代码似乎意味着我们最终还可以将类型为Cat的值分配给类型为Dog的变量或类似的东西.
你必须考虑类的结构.T extends Throwable而你正在Exception转向它.这就好比分配Dog给Animal不Dog给Cat.
编译器具有关于哪些Throwable被检查以及哪些不是基于继承的规则.这些是在编译时应用的,并且可能会混淆编译器以允许您抛出检查异常.在运行时,这没有任何影响.
已检查的异常是编译时功能(如泛型)
BTW Throwable也是一个经过检查的例外.如果你对它进行子类化,它将被检查,除非它是Error或RuntimeException的子类.
另一种抛出已检查异常的方法,而无需编译器知道您正在执行此操作.
Thread.currentThread().stop(throwable);
Unsafe.getUnsafe().throwException(throwable);
Run Code Online (Sandbox Code Playgroud)
唯一的区别是两者都使用本机代码.
从 Java 8 开始,sneakyThrowInner不再需要 helper 方法。 sneakyThrow可以写成:
@SuppressWarnings("unchecked")
static <T extends Throwable> RuntimeException sneakyThrow(Throwable t) throws T {
throw (T)t;
}
Run Code Online (Sandbox Code Playgroud)
请参阅“ Java 8 中异常类型推断的一个特殊特性”一文。
SnekyThrow 的 T 被推断为 RuntimeException。这可以从关于类型推断的语言规范(http://docs.oracle.com/javase/specs/jls/se8/html/jls-18.html)中遵循
注意:该sneakyThrow函数声明为 return RuntimeException,因此可以按如下方式使用:
int example() {
if (a_problem_occurred) {
throw sneakyThrow(new IOException("An I/O exception occurred"));
// Without the throw here, we'd need a return statement!
} else {
return 42;
}
}
Run Code Online (Sandbox Code Playgroud)
通过抛出RuntimeException返回的 by sneakyThrow,Java 编译器知道此执行路径终止。(当然,sneakyThrow它本身不会返回。)