1.何为协变
假设有一个接口,以及一个他的实现类
如下:
接口为:
public interface GenericsInterface {
void test();
}
其实现类为:
public class Type2 implements GenericsInterface{
int i = 2;
public void test(){
System.out.println(i);
}
}
则
//子类的对象可以直接为父类的句柄进行引用,即为是可以协变的
GenericsInterface gi = new Type2();
2.泛型是不能协变的
在1的情况下添加一个类G1
public class G1<T> {
private T i = null;
public void print(){
System.out.println(i);
}
public T getI() {
return i;
}
public void setI(T i) {
this.i = i;
}
}
测试代码如下:
//子类的对象可以直接为父类的句柄进行引用,即为是可以协变的
GenericsInterface gi = new Type2();
//以子类SUBTYPE作为泛型参数的A类对象
//是不能由以SUBTYPE的父类作为泛型参数的A类句柄所引用,即为不可协变
G1<GenericsInterface> g4 = new G1<Type2>();//invalid
[color=red]由1,2两点可以推出以下的结论:
(1)子类的对象可以直接为父类的句柄进行引用,即为是可以协变的
(2)以子类SUBTYPE作为泛型参数的A类对象是不能由以SUBTYPE的父类TYPE作为泛型参数的A类句柄所引用,即为不可协变[/color]
3.java为什么要让泛型不能协变呢?
请看以下的示例
List<Integer> li = new ArrayList<Integer>();
List<Number> ln = li; // illegal
ln.add(new Float(3.1415));
[color=blue]因为 ln是 List<Number>,所以向其添加 Float似乎是完全合法的。但是如果 ln是 li的别名,那么这就破坏了蕴含在 li定义中的类型安全承诺 —— 它是一个整数列表,这就是泛型类型不能协变的原因。[/color]
4.泛型不能协变导致的问题
如下示例
G1[] g1array = new G1[3];
G1<String>[] g1array2 = new G1<String>[3];//invalid
G1<?>[] g1array3 = new G1<?>[3];
为什么当数组中的G1类的泛型参数具体化后变成invalid的呢?
也就是说在实例化G1数组,往G1数组添加成员之前,就把泛型参数具体指定时,是非法的
由以下可以看出原因
List<String>[] lsa = new List<String>[10]; // illegal
Object[] oa = lsa;//OK because List<String> is a subtype of Object
List<Integer> li = new ArrayList<Integer>();
li.add(new Integer(3));
oa[0] = li;
String s = lsa[0].get(0);
最后一行将抛出 ClassCastException,因为这样将把 List<Integer>填入本应是 List<String>的位置。因为数组协变会破坏泛型的类型安全,所以不允许实例化泛型类型的数组(除非类型参数是未绑定的通配符,比如 List<?>)。