Java泛型:为什么这个输出可能?

Soc*_*cke 51 java generics intellij-idea jshell

我有这门课:

class MyClass<N extends Number> {
    N n = (N) (new Integer(8));
}
Run Code Online (Sandbox Code Playgroud)

我想得到这些输出:

System.out.println(new MyClass<Long>().n);
System.out.println(new MyClass<Long>().n.getClass());
Run Code Online (Sandbox Code Playgroud)
  1. 第一项System.out.println()陈述的输出:

    8
    
    Run Code Online (Sandbox Code Playgroud)
  2. 输出第二个System.out.println()陈述:

    java.lang.ClassCastException: java.lang.Integer (in module: java.base)
        cannot be cast to java.lang.Long (in module: java.base)
    
    Run Code Online (Sandbox Code Playgroud)

为什么我得到第一个输出?是不是也有演员?为什么我在第二个输出中得到异常?

PS:我使用的是Java 9; 我用JShell尝试了它,我在两个输出上都有一个例外.然后我尝试使用IntelliJ IDE并获得第一个输出,但第二个输出异常.

Cal*_*tor 39

IntelliJ显示的行为对我来说很清楚:

你有一个未经检查的演员MyClass.当执行此行时,这意味着new Integer(8)不会立即Long转换为擦除Number(有效):N n =(N)(new Integer(8));

现在让我们看一下输出语句:

System.out.println(new MyClass<Long>().n);
Run Code Online (Sandbox Code Playgroud)

归结为String.valueOf(new MyClass<Long>().n)- > ((Object)new MyClass<Long>().n).toString()工作正常,因为n是通过访问的方式Object,并且toString()方法是通过静态类型访问Object- >没有强制转换Long.new MyClass<Long>().n.toString()会因异常而失败,因为toString()尝试通过静态类型访问Long.因此,Long发生n到类型的转换,这是不可能的(Integer不能转换为Long).

执行第二个语句时会发生同样的事情:

System.out.println(new MyClass<Long>().n.getClass()); 
Run Code Online (Sandbox Code Playgroud)

试图通过静态类型访问类型的getClass方法(声明Object).因此,会发生n到类型的转换,这会产生强制转换异常.LongLongLong

JShell行为:

我试图在JShell上重现第一个输出语句的结果异常 - Java 9早期访问Build 151:

jshell> class MyClass<N extends Number> {
   ...>     N n = (N) (new Integer(8));
   ...> }
|  Warning:
|  unchecked cast
|    required: N
|    found:    java.lang.Integer
|      N n = (N) (new Integer(8));
|                ^--------------^
|  created class MyClass

jshell> System.out.println(new MyClass<Long>().n);
8

jshell> System.out.println(new MyClass<Long>().n.getClass());
|  java.lang.ClassCastException thrown: java.base/java.lang.Integer cannot be cast to java.base/java.lang.Long
|        at (#4:1)
Run Code Online (Sandbox Code Playgroud)

但似乎JShell提供了与IntelliJ完全相同的结果.System.out.println(new MyClass<Long>().n);输出8 - 没有例外.

  • @ArthurM.编译器没有问题.由于类型转换为`(N)`,它正确编译 (2认同)
  • @RudolphEst是因为编译器将`new MyClass <Long>().n`视为Long(当它实际上是一个Integer时),但不需要强制转换它,因为它被分配给一个超类Object.但调用`new MyClass <Long>().n.anyMethodNameHere()`要求编译器实际执行`((Long)(new MyClass <Long>().n)).anyMethodNameHere()`.这是Java泛型和类型擦除的混乱. (2认同)
  • 好吧,我必须承认我没有将我的`new MyClass <Long>().n'放入'System.out.println();' ,因为通常JShell打印变量...但当然JShell以这种方式保存它:'$ 1 = new MyClass <Long>().n'这可能是我迷茫的原因! (2认同)

Rap*_*aël 25

这是因为Java擦除而发生的.

Integer扩展以来Number,编译器接受转换为N.在运行时,由于(由于擦除)N取代Number,因此存储Integer内部没有问题n.

方法的参数System.out.println是类型,Object因此打印值的方法没有问题n.

但是,在调用方法时n,编译器会添加类型检查以确保调用正确的方法.因此导致了ClassCastException.