Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/329.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/17.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 为什么我可以在Scala中定义泛型异常类型?_Java_Scala_Jvm - Fatal编程技术网

Java 为什么我可以在Scala中定义泛型异常类型?

Java 为什么我可以在Scala中定义泛型异常类型?,java,scala,jvm,Java,Scala,Jvm,在Java中,定义泛型异常类是非法的。编译器将拒绝编译以下内容: public class Foo<T> extends Throwable { // whatever... } 更奇怪的是,只要捕获原始Foo类型,我就可以在Java代码中使用这个Scala类: public class Main { public static void main(String[] args) { try { throw new Foo<

在Java中,定义泛型异常类是非法的。编译器将拒绝编译以下内容:

public class Foo<T> extends Throwable {
    // whatever...
}
更奇怪的是,只要捕获原始
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());
        }
    }
}
公共类主{
公共静态void main(字符串[]args){
试一试{
抛出新Foo(“测试”);
}
捕获(Foo-e){
System.out.println(e.foo());
}
}
}
这将编译、运行和打印“测试”

这是根据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
}
公共类Foo扩展了java.lang.Throwable{
公共价值观();
代码:
0:aload_0
1:getfield#15//字段值:Ljava/lang/Object;
4:轮到你了
公共食品(T);
代码:
0:aload_0
1:aload_1
2:putfield#15//字段值:Ljava/lang/Object;
5:aload_0
6:invokespecial#22//方法java/lang/Throwable。“:()V
9:返回
}

以下是我们在讨论中得出的答案

这是根据JLS和JVM规范定义的,还是仅仅根据JLS和JVM规范定义的 偶然工作

它不必根据Java语言规范进行很好的定义,因为它是一种不同的语言

JVM OTOH在抛出和捕获此类异常方面没有问题,因为由于类型擦除,它对字节码没有影响

然而,有趣的问题仍然存在,为什么Java首先禁止使用泛型类扩展Throwable。

简短回答:

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

长答覆:

Java语言规范(§8.1.2)说明了如何声明此类:

如果泛型类是Throwable(§11.1.1)的直接或间接子类,则为编译时错误

由于Java虚拟机的捕获机制仅适用于非泛型类,因此需要此限制

它说的是关于抛出异常(§14.18):

throw语句中的表达式必须表示

1) 可分配(§5.2)给可丢弃类型的参考类型的变量或值,或

2) 出现空引用或编译时错误

表达式的引用类型将始终是未参数化的类类型(因为没有接口类型可分配给Throwable)(因为Throwable的子类不能是泛型的(§8.1.2))

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

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

try {
    throw new Foo<String>("test");
} catch(Foo<Int> e) {
  // int
} catch(Foo<String> e) {
  // string
}
试试看{
抛出新Foo(“测试”);
}捕获(Foo-e){
//int
}捕获(Foo-e){
//串
}
异常捕获的实现方式是有一个异常表,指定要监视的字节码范围和要捕获的关联类。该类不能有类型参数,因为无法用字节码描述它们(JVM规范,§2.10和§3.12)

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


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

显然,Scala生成的字节码是有效的,否则任何Scala程序都无法运行。我认为这并不明显。即使在Scala中,通常也不定义泛型异常类型。即使你这样做了,仅仅因为它在主流JVM上工作并不意味着它根据规范是有效的。你考虑过类型擦除吗?无论抛出泛型还是非.generic类型的对象,字节码都不会有任何区别。就JVM而言,泛型类型根本不存在。字节码中仍然有一些信息表明这是一个泛型类(请参阅我的编辑)。我看不到抛出。我声称:当你抛出时,字节码不会有什么不同。您提到的额外信息只是为了支持反射工具,如IDE、debugger或javap,它们对JVM没有任何作用。我猜“如果泛型类是Throwable的直接或间接子类,这是一个编译时错误”只适用于类的定义,而不适用于
throw new Foo(“test”)中的使用?正确,但它错误地认为,正因为如此,
抛出新的Foo(“测试”)
无论如何都不会发生,请参见编辑。
try {
    throw new Foo<String>("test");
} catch(Foo<Int> e) {
  // int
} catch(Foo<String> e) {
  // string
}