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
- 为什么
b.get()
没有触发运行时错误?
- 为什么只有当我尝试获取 class 变量的 class 时才会出现运行时错误?
更准确地说:为什么编译器在第二个仅字节码中插入checkcast
指令get()
(导致异常)?
在代码行中Box<Integer> b = new Box(new String("may be"));
在创建新的 Box
对象时缺少类型信息并假设为默认的 Object
class。
Box<String> b = new Box<>(new String());
这是正确的做法
--------已添加--------
在Object
class的方法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){}
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
- 为什么
b.get()
没有触发运行时错误? - 为什么只有当我尝试获取 class 变量的 class 时才会出现运行时错误?
更准确地说:为什么编译器在第二个仅字节码中插入checkcast
指令get()
(导致异常)?
在代码行中Box<Integer> b = new Box(new String("may be"));
在创建新的 Box
对象时缺少类型信息并假设为默认的 Object
class。
Box<String> b = new Box<>(new String());
这是正确的做法
--------已添加--------
在Object
class的方法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){}