【发布时间】:2013-07-30 01:57:50
【问题描述】:
在 Java 中,定义泛型异常类是非法的。编译器将拒绝编译以下内容:
public class Foo<T> extends Throwable {
// whatever...
}
但是,这个 Scala 代码编译得很好:
class Foo[T](val foo: T) extends Throwable
更奇怪的是,只要我捕捉到原始的Foo 类型,我就可以在 Java 代码中使用这个 Scala 类:
public class Main {
public static void main(String[] args) {
try {
throw new Foo<String>("test");
}
catch(Foo e) {
System.out.println(e.foo());
}
}
}
这会编译、运行和打印“test”。
这是根据 JLS 和 JVM 规范明确定义的,还是只是偶然发生的?
Java 对泛型异常的限制纯粹是一种语言限制,还是它也适用于字节码(在这种情况下,Scala 编译器生成的字节码将无效)?
编辑:这是 Scala 类在反编译后的样子:
public class Foo<T> extends java.lang.Throwable {
public T value();
Code:
0: aload_0
1: getfield #15 // Field value:Ljava/lang/Object;
4: areturn
public Foo(T);
Code:
0: aload_0
1: aload_1
2: putfield #15 // Field value:Ljava/lang/Object;
5: aload_0
6: invokespecial #22 // Method java/lang/Throwable."<init>":()V
9: return
}
【问题讨论】:
-
显然Scala生成的字节码是有效的,否则没有Scala程序可以运行。
-
我认为这并不明显。即使在 Scala 中,您通常也不定义通用异常类型。即使你这样做了,仅仅因为它适用于主流 JVM 并不意味着它根据规范是有效的。
-
你考虑过类型擦除吗?无论您抛出一个泛型或非泛型类型的对象,字节码都没有区别。就 JVM 而言,泛型类型根本不存在。
-
字节码中仍有一些信息表明这是一个泛型类(参见我的编辑)。
-
我没有看到 throw。我声称:当你抛出时,它不会对字节码产生任何影响。你提到的额外信息只是为了支持反射工具,如 IDE、调试器或 javap,它们对 JVM 没有任何作用..