Java原始类型值分配给泛型运行时getClss()方法错误

Java raw type value assigned to generic type run time getClss() method error

public class Box<T> {
    private T t;
    public Box(T t){
        this.t = t;
    }
    public void add(T t) {
      this.t = t;
    }
    public T get() {
      return t;
    }
    public static void main(String[] args) {
      Box<Integer> b = new Box(new String("may be"));
      System.out.println(b.get()); // successfully print out "may be"
      System.out.println(b.get().getClass()); // error
   }
}

此代码给出运行时错误:

exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
  1. 为什么 b.get() 没有触发运行时错误?
  2. 为什么只有当我尝试获取 class 变量的 class 时才会出现运行时错误?

更准确地说:为什么编译器在第二个字节码中插入checkcast指令get()(导致异常)?

在代码行中Box<Integer> b = new Box(new String("may be")); 在创建新的 Box 对象时缺少类型信息并假设为默认的 Object class。

Box<String> b = new Box<>(new String());这是正确的做法

--------已添加--------

Objectclass的方法getClass()return中运行时Class对象。当 returning 时,您的代码 Box<Integer> 使 getClass() 方法变为 return Class<Integer> 而不是 Class<String>

您使用 new B(new String()); 给出了原始类型,因此编译器通过了语法,但是在调用 getClass() 时它将内部对象 String 转换为 Integer 并抛出 RuntimeException

请注意:

  • 在第一种情况下,get()的结果用于println(Object):换句话说:接收方期望一个对象,并且“条件”将始终为真。
  • 在第二种情况下,将对返回的对象执行 方法 调用。现在,如果返回的类型是 expected 类型,它会产生 huge 差异。因此,编译器正在添加此检查以保证以下方法调用是 sound.

作为背景,可以查看 Java 语言规范,第 5.52 章:

The cast is a checked cast.

Such a cast requires a run-time validity check. If the value at run time is null, then the cast is allowed. Otherwise, let R be the class of the object referred to by the run-time reference value, and let T be the erasure (§4.6) of the type named in the cast operator. A cast conversion must check, at run time, that the class R is assignment compatible with the type T, via the algorithm in §5.5.3.

分别是第5.53章Checked Casts at Run Time.

这个变量声明不一致。

  Box<Integer> b = new Box(new String("may be")); :

实例化的对象是原始的,因此编译器会发出警告,但允许将原始类型分配给通用变量:Box<Integer>.

b.get() 不会失败,因为您没有将结果分配给变量。
所以编译器不需要将它转换为任何东西。

试试看:

Integer value = b.get();

它可以正常编译,但你会在运行时得到同样的异常 JVM 将尝试将该值转换为 Integer:

java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer

当您调用时:

System.out.println(b.get().getClass()); // error

东西都接近了。

为什么这里需要 Integer class?

在编译时,b 声明为 Box<Integer> b
所以编译器知道 b 是用 Integer 类型参数化的。
所以是的,在编译之后,泛型被删除,但是编译器根据声明的代码添加了一些强制转换。

这里就是这种情况。
您在使用 Integer 参数化的变量上调用 getClass()
但是 Class 是一个泛型 class :Class<T>.
所以编译器添加了对 Integer 的转换以符合 Class<Integer>.

当然,通过对变量声明和实例化都使用泛型:

  Box<Integer> b = new Box<>(new String("may be"));

这种不一致是不可能的,因为编译器现在会阻止你。

除了此处的答案外,字节指令 checkcast 在类型检查中被调用,这不是 Object 的具体类型。例如

 public static void main(String[] args) {
          Box<Integer> b = new Box(new String("may be"));
          doStuff(b.get()); // no checkcast needed - works fine
          doIntegerStuff(b.get()); // run-time error with checkcast
          doStringStuff(b.get()); // compile error
       }

 public static void doStuff(Object object){}
 public static void doStringStuff(String integer){}
 public static void doIntegerStuff(Integer integer){}