gpt4 book ai didi

java - 为什么我可以在 Scala 中定义通用异常类型?

转载 作者:塔克拉玛干 更新时间:2023-11-03 05:06:13 24 4
gpt4 key购买 nike

在 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
}

最佳答案

简答:

  • JVM 规范禁止抛出和捕获参数化异常,但不关心声明。它甚至不禁止这样做,只是没有办法在字节码中表示类型参数,所以这个问题没有实际意义。
  • JLS 禁止声明它们,因为无论如何您都无法使用它们。

长答案:

Java 语言规范说 (§8.1.2) 关于声明这样一个类:

It is a compile-time error if a generic class is a direct or indirect subclass of Throwable (§11.1.1).

This restriction is needed since the catch mechanism of the Java Virtual Machine works only with non-generic classes.

它说抛出一个异常(§14.18):

The Expression in a throw statement must denote either

1) a variable or value of a reference type which is assignable (§5.2) to the type Throwable, or

2) the null reference, or a compile-time error occurs.

The reference type of the Expression will always be a class type (since no interface types are assignable to Throwable) which is not parameterized (since a subclass of Throwable cannot be generic (§8.1.2)).

这个限制是在将泛型添加到 Java 语言时添加的,因为它们没有添加到 JVM 本身:JVM 上只存在原始类型。

仍然有关于定义类的那个类型参数的信息,但是没有使用的地方!独立于参数化异常的声明,这在 JVM 级别是不可能的:

try {
throw new Foo<String>("test");
} catch(Foo<Int> e) {
// int
} catch(Foo<String> e) {
// string
}

异常捕获的实现方式是有一个异常表,指定要监视的字节码范围和要捕获的关联类。该类不能有类型参数,因为无法在字节码中描述它们(JVM 规范,§2.10 和 §3.12)。

由于类型删除,throw 子句仅引用 Foo,这两个 catch 方法将成为异常表中的两个条目,它们都引用类 Foo ,这既无用又不可能。因此,在 Java 语言中语法是不可能的,只能捕获 Foo

因此,能够声明参数化异常变得非常无用且具有潜在危险。所以它们在语言中被完全禁止,即使 JVM 本身并不关心。

关于java - 为什么我可以在 Scala 中定义通用异常类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17921403/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com