当前位置: 首页 > 面试题库 >

Java SneakyThrow抛出异常,类型擦除

苏鸿志
2023-03-14
问题内容

有人可以解释此代码吗?

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


}

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

注意,sneakyThrow(...)或者main都没有声明任何检查过的异常,但是输出是:

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)

在Lombok中使用此hack,带有@SneakyThrow批注,该批注允许抛出已检查的异常而无需声明它们。

我知道它与类型擦除有关,但是我不确定要了解hack的每个部分。

编辑: 我知道我们可以在中插入IntegerList<String>并且选中/未选中的异常区别是编译时功能。

当从非通用类型List转换为通用类型时List<XXX>,编译器会产生警告。但是直接像(T) ex上面的代码中那样直接转换为泛型类型的情况并不常见。

如果您愿意,对我来说很奇怪的部分是,我了解JVM
a内的内容,List<Dog>并且List<Cat>看起来相同,但是上面的代码似乎意味着最终我们也可以将Cat类型的值分配给Dog类型的变量或这样的事情。


问题答案:

如果使用它进行编译,-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

基本上是说“ 在执行时并没有 真正 检查这个强制转换”(由于类型擦除)-因此编译器无奈地假设您做的是正确的,知道它实际上不会被检查。

现在只有 编译器 关心已检查和未检查的异常-它根本不是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...


 类似资料:
  • 我是jUnit和mockito的新手。对于虚空的嘲弄是如何发挥作用的,我完全感到困惑。在这里,如果名称是“hello”,则函数抛出一个异常。但当我测试它时,它并没有抛出异常... 测试 }

  • 问题内容: 我试图在Netbeans中重构一个大型程序,但我有点迷茫。我从来没有非常模块化,但是现在通过实际学习如何做到这一点来尝试纠正这种情况,并在将来纠正这种情况。不幸的是,我在将某些教程翻译成我的程序时遇到了麻烦。所以我希望这里有人可以帮忙。目前,我正在尝试分解一部分采用特定格式的文件并制成表格的代码。我知道我需要创建一个类并使用它来创建表对象,但是我不确定如何做。我有一个主文件,用于获取文

  • 抛出异常的行为是否可能抛出不同的异常? 为了抛出异常,必须(可选地)分配新对象,并调用其构造函数(隐式调用fillinstacktrace)。在某些情况下,听起来像addSupressed也被称为。那么如果没有足够的内存会发生什么呢?JVM是否需要预分配内置异常?例如,(1/0)会抛出OutOfMemoryError而不是ArithmeticException吗? 此外,构造函数是一个方法调用,因

  • 问题内容: 我想捕获一个异常,将其记录下来,设置一个标志,然后重新抛出相同的异常 我有这个代码: 但是eclipse在throw ex时抱怨,说“未处理的异常类型Exception”,建议我在它周围添加一个try-catch块。 实际上,我希望调用此方法的进程处理该异常,而不是自己处理……如果一切正常,我只想返回true,如果有异常,则将其记录下来 另一方面,我可以将异常包装在另一个异常中,但是不

  • 问题内容: 考虑以下代码: 无需添加方法签名即可编译该代码。(它与同样表现到位,太)。 我理解为什么 可以 安全地运行它,因为实际上不能将其引发在块中,因此不能引发已检查的异常。我有兴趣知道在何处指定此行为。 并非永远都不会达到目标:以下代码也会编译: 但是,如果抛出一个检查的异常,它不会像我期望的那样编译: 在JLS Sec 11.2.2中 ,它说: 一,其抛出的表达式语句(§14.18)具有静

  • 在你可以捕获异常之前,一些代码必须抛出一个异常。任何代码都可能会抛出异常:您的代码,来自其他人编写的包(例如Java平台附带的包)或Java运行时环境的代码。无论是什么引发的异常,它总是通过 throw 语句抛出。 您可能已经注意到,Java平台提供了许多异常类。所有类都是Throwable类的后代,并且都允许程序区分在程序执行期间可能发生的各种类型的异常。 您还可以创建自己的异常类来表示在您编写