对泛型之不能协变(convariant)的理解,以及不能协变导致的问题

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<?>)。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值