Java SneakyThrow异常,键入擦除 [英] Java SneakyThrow of exceptions, type erasure

查看:295
本文介绍了Java SneakyThrow异常,键入擦除的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有人可以解释一下这段代码吗?

 公共类SneakyThrow {


public static void sneakyThrow(Throwable ex){
SneakyThrow。< RuntimeException> sneakyThrowInner(ex);
}

private static< T extends Throwable> T sneakyThrowInner(Throwable ex)抛出T {
throw(T)ex;
}



public static void main(String [] args){
SneakyThrow.sneakyThrow(new Exception());
}


}

似乎很奇怪,但是这不会产生强制转换异常,并且允许抛出已检查的异常而不必在签名中声明它,或者将其包装在未经检查的异常中。



<请注意, sneakyThrow(...)或主要声明任何已检查的异常,但输出为:

 线程main中的异常java.lang.Exception 
at com.xxx.SneakyThrow.main(SneakyThrow.java:20)
at sun.reflect .NativeMethodAccessorImpl.invoke0(原生方法)
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)

此hack在Lombok中使用,注释@SneakyThrow,允许抛出已检查的异常而不声明它们。






我知道它与类型擦除有关,但我不确定理解黑客的每一部分。






编辑:
我知道我们可以插入 List< String> 中的整数,并且已检查/未检查的异常区别是编译时功能。



从非泛型类型(如 List )转换为类似列表< XXX> 编译器产生警告。但是在上面的代码中直接转换为泛型类型(例如(T)ex )并不常见。



如果你想要的,对我来说似乎很奇怪的部分是我理解JVM中的一个 List< Dog> List< Cat> 看起来一样,但上面的代码似乎意味着最后我们还可以将类型Cat的值赋给类型为Dog的变量或类似的东西。

解决方案

如果用 -Xlint 编译它,你会收到警告:

  c:\ Users \ Jon @\\ Test> javac -Xlint SneakyThrow.java 
SneakyThrow.java:9:警告:[未选中]未选中强制转换
throw(T)ex;
^
required:T
found:Throwable
其中T是一个类型变量:
T extends在方法中声明的Throwable< T> sneakyThrowInner(Throwable)
1警告

这基本上是说这个演员不是真的在执行时检查(由于类型擦除) - 所以编译器不情愿地假设你正在做正确的事情,知道它实际上不会被检查。



现在只有编译器关注已检查和未检查的异常 - 它根本不是JVM的一部分。所以一旦你通过编译器,你就可以免费回家了。



我强烈建议你不要这样做。



在许多情况下,当您使用泛型时会进行真实检查,因为某些内容使用了所需的类型 - 但这并非总是 。例如:

  List< String> strings = new ArrayList< String>(); 
列出raw = strings;
raw.add(new Object()); // 哈哈!我在List< String>中放了一个非String。
Object x = strings.get(0); //这不需要演员,所以没有例外...


Can someone explain this code?

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());
  }


}

It may seems strange, but this doesn't produce a cast exception, and permits to throw a checked exception without having to declare it in the signature, or to wrap it in an unchecked exception.

Notice that neither sneakyThrow(...) or the main are declaring any checked exception, but the output is:

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)

This hack is used in Lombok, with the annotation @SneakyThrow, which permits to throw checked exceptions without declaring them.


I know it has something to do with type erasure, but i'm not sure to understand every part of the hack.


Edit: I know that we can insert an Integer in a List<String> and that checked/unchecked exceptions distinction is a compile time feature.

When casting from a non-generic type like List to a generic type like List<XXX> the compiler produces a warning. But it's less common to cast to a generic type directly like (T) ex in the above code.

If you want, the part that seems strange for me is that I understand that inside the JVM a List<Dog> and List<Cat> looks the same, but the above code seems to mean that finally we can also assign a value of type Cat to a variable of type Dog or something like that.

解决方案

If you compile it with -Xlint you'll get a warning:

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

That's basically saying "This cast isn't really checked at execution time" (due to type erasure) - so the compiler reluctantly assumes you're doing the right thing, knowing that it won't actually be checked.

Now it's only the compiler which cares about checked and unchecked exceptions - it's not part of the JVM at all. So once you've got past the compiler, you're home free.

I'd strongly advise you to avoid doing this though.

In many cases there's a "real" check when you're using generics because something uses the desired type - but that's not always the case. For example:

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...

这篇关于Java SneakyThrow异常,键入擦除的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆