每隔一段时间,我觉得我根本不懂Java ..我最近发现了Java类型转换的奇怪行为:
public static void main(String[] args) {
String res = get();
System.out.println(res);
}
public static <T> T get() {
Object longObj = Long.valueOf("0");
T casted = (T) longObj;
System.out.println("longObj=" + longObj.getClass());
System.out.println("casted=" + casted.getClass()); // <-- Why the type of "casted" is Long instead of String???
return casted;
}
输出结果为:
longObj=class java.lang.Long
casted=class java.lang.Long
Exception in thread "main" java.lang.ClassCastException: java.lang.Long
cannot be cast to java.lang.String
at com.apple.geo.coreloc.Test.main(Test.java:5)
我的困惑是:为什么变量casted
的类型为Long
?是不是应该输入T
,即String
?
以下是为什么我认为它应该是String
:
String res = get()
提示get()方法返回String T casted = (T) longObj;
应该尝试将强制类型转换为字符串(我希望这里有一个例外,但是没有&#39; t ..)答案 0 :(得分:3)
泛型是编译时间检查。它们不是编写坏演员的保障措施。
您尝试(String) longIbj;
,其中longObj
很长。那会给你一个运行时ClassCastException
。
但是,因为在T get()
的上下文中没有编译时错误,所以T get()
被正确编译。由于&#34;类型擦除&#34; T被提升为最高约束类型。您没有限制类型的上限,因此编译器处理的代码大致如下:
public static Object get() {
Object longObj = Long.valueOf("0");
Object casted = (Object) longObj;
System.out.println("longObj=" + longObj.getClass());
System.out.println("casted=" + casted.getClass()); // <-- Why the type of "casted" is Long instead of String???
return casted;
}
由于<T>
是Object
,填充longObj
和casted
的两行代码大致相同。在任何情况下,通过名称对对象引用的多态调用仍将解析为对象的.getClass()
方法,该方法仍将返回java.lang.Long
。
使用Java进行强制转换不会创建新对象。如果您想要一个不同的对象,则需要new
一个不同的对象并用相关信息填充它。使用java进行强制转换只能通过强制转换类型的方法名访问该类。因此,getLong()
投放Long
可能会失去Object
,但它仍然存在(如果您通过Long
类型的变量调用它。
答案 1 :(得分:2)
投射与转化不同。
Casting是一个声明,您的对象IS是目标类型。它不会改变当前对象的类。
转换是将一种类型的对象重新创建为另一种类型的对象的过程。
这就是为什么只有在您已经知道要投射的对象的类型时才应该使用。在所有其他情况下,您应该将对象转换为目标类型的实例。
答案 2 :(得分:1)
在Java中,泛型类型仅在编译时起作用,但它(几乎)没有关于特定目标泛型类型的信息,因为所谓的“类型擦除”(参见here和here)。
如果您反编译代码,您会看到:
public static void main(String[] args) {
String res = (String)get();
System.out.println(res);
}
public static <T> T get() {
Object longObj = Long.valueOf("123");
System.out.println("longObj=" + longObj.getClass());
System.out.println("casted=" + longObj.getClass());
return longObj;
}
这一切都很清楚:
main
中,它只是强制转换操作get
中,带有“已投放”值的变量已完全删除答案 3 :(得分:1)
Java泛型与C ++模板不同。对于Java泛型方法,该方法只有一个版本。在C ++中,根据特定类型的需要生成模板化方法的代码,以便每个不同的参数化类型获得它自己的方法版本。
要获得您期望的错误,您必须通过告诉它通用类型来帮助Java。
class A {
public static void main(String[] args) {
String res = get(String.class);
System.out.println(res);
}
public static <T> T get( Class<T> klass) {
Object longObj = Long.valueOf("0");
T casted = klass.cast(longObj);
System.out.println("longObj=" + longObj.getClass());
System.out.println("casted=" + casted.getClass());
return casted;
}
}
运行此操作会产生强制转换异常:
Exception in thread "main" java.lang.ClassCastException: Cannot cast java.lang.Long to java.lang.String
at java.lang.Class.cast(Class.java:3369)
at A.get(A.java:10)
at A.main(A.java:4)