通用方法和通用类中的类型推断
问题描述:
无法理解Java中的泛型编程。通用方法和通用类中的类型推断
我看了一些关于它的教程,但仍然很困惑,尤其是当事情变得复杂时。
任何人都可以解释这个例子中发生了什么?
import java.util.Date;
public class Test1 {
public static void main(String[] args) {
P<Cls> p = new P<>(); //<1> //I expect a ClassCastException here, but no. Why? //How does the type inference for class P<E> work?
System.out.println(p.name); //it prints
// System.out.println(p.name.getClass());//but this line throws ClassCastException //why here? why not line <1>?
test1(p);//it runs
// test2(p);//throws ClassCastException//What is going on in method test1&test2?
//How does the type inference for generic methods work in this case?
}
public static<T> void test1(P<? extends T> k){
System.out.println(k.name.getClass());
}
public static<T extends Cls> void test2(P<? extends T> k){
System.out.println(k.name.getClass());
}
}
class P<E>{
E name = (E)new Date();//<2>
}
class Cls{}
答
P<Cls> p = new P<>();
记住的Java由擦除,这意味着为P
构造并没有真正有任何想法E
在运行时实现仿制药。 Java中的泛型纯粹是为了在编译时帮助开发人员。
这意味着,当你创建一个new P<>()
,一个new Date()
被创建,但实际上并没有转换为任何特定类型的,因为运行时不知道E
什么。 E
在运行时不存在,就类而言。 name
只是一个Object
参考,里面有一个Date
。但是,只要运行时环境需要知道它是特定类型(在本例中为Cls
),编写代码时使用name
时,编译器会在不告诉您的情况下将该类型转换为该类型。
-
p.name.getClass()
被编译为((Cls)p.name).getClass()
,这将创建一个类转换异常。 -
test2()
指定了非通用类型约束(extends Cls
)。所以其致电p.name.getClass()
同样被翻译为((Cls)p.name).getClass()
。
在另一方面:
-
System.out.println(p.name)
实际上是相同的,因为System.out.println((Object)p.name)
是println
一个非通用方法,它有一个object
。 -
test1(p.name)
与此类似。由于运行时间实际上并不知道T
是什么类型,因此在调用getClass()
之前,它基本上将p.name
作为Object
。
换句话说,这里是你的代码,因为它实际上是被编译:
class P{
Object name = new Date();
}
public static void main(String[] args) {
P p = new P();
System.out.println(p.name);
System.out.println(((Cls)p.name).getClass());
test1(p);
test2(p);
}
public static void test1(P k){
System.out.println(k.name.getClass());
}
public static void test2(P k){
System.out.println(((Cls)k.name).getClass());
}
这说明*它发生时*,但它并不能解释为什么* *。为什么'p.name.getClass()'涉及到一个强制转换,而不是'p.name'?即为什么不把第一行编译为'System.out.println((Cls)p.name);'? – 2014-10-11 12:47:06
@OliverCharlesworth:因为Java希望运行时知道编译器知道该对象的什么,以便调用它的方法。在这种情况下,编译器绘制的笔画比所需的更宽,因为'getClass()'恰好在所有'Objects'上都可用,但是如果它是在'Cls'上声明的方法,那么类转换就是必需的。另一方面,将一个对象传递给一个需要'Object'的方法不需要投射。 – StriplingWarrior 2014-10-11 12:56:42
好的,我们所说的确实是编译器在某些情况下不依赖于上下文而使用cast。问题是,管理这个的规则是什么?想必它不是随机的。 (这是一个真正的问题;我无法弄清楚JLS中可能指定的地方等) – 2014-10-11 13:02:56