// 1.协变范例
List<? extends Number> num = new List<Double>();
// 2.逆变范例
List<? super Number> num = new List<Object>();
但是在第一种情况中就不能呼叫参数里带有?的函数了,例如:
class Container<E> {
E data;
public set(E data) {
this.data = data;
}
public E get(E data) {
return data;
}
}
// 某方法中
Container<? extends Number> num = new Container<Double>();
num.set(new Float(3)); // 编译时错误
这里同样是为了保证Type Safety。不能把Float传给Container<Double>。
同理,在逆变的时候就不能呼叫返回值带?的函数,除非接受返回值的变量类型是Object。
Container<? super Number> num2;
Number res = num2.get(); // 编译时错误
注意对比的对象
A是B的子类 是A与B在比较
而
List<A>
是List<B>
的子类 是List与List进行比较你可以通过
List<? extend b>
这样的定义 来进行限定操作因为都是List类,又没有继承关系
对象的继承代表了一种‘is-a’的关系,如果两个对象A和B,可以表述为‘B是A’,则表明B可以继承A。继承者还可以理解为是对被继承者的特殊化,因为它除了具备被继承者的特性外,还具备自己独有的个性。
父类和子类,或叫做基类和派生类,其中子类继承父类的所有特性,同时还定义新的特性。
List<A>
的子类是class XXX extends List<A>
Java里的泛型是通过 类型擦除 来实现的
以下仅供参考 我C++不熟
你不是和C++模板搞混了?
C++的泛型实现方式不同
因为
List<A>.class
不成立。泛型
一大堆回答都没讲到重点啊。。我来加点内容。这里涉及到两个概念:协变与逆变。(Covariance vs. Contravariance)
简言之,如果
List<S>
复合类型是协变的,那么如果S
是T
的子类,则List<S>
也是List<T>
的子类。如果List<S>
是逆变的,结果颠倒,List<T>
会变成List<S>
的子类。但是Java里的泛型不实现以上两种行为的任意一种。。Java的泛型是不变的(Invariance)。
为什么这么设计呢?先看一个数组的例子。在Java中,数组是协变的,于是
Integer[]
就是Number[]
的子类了。于是我们能写出这样的代码:Java的类型安全(Type Safety)的保证已经被破坏了。Java会把数组的元素应有类型保存起来(Reification),保证能在运行时检测到这种非法操作。
假设List是协变的,那么如下把Float放进Integer的代码:
就能运行。而 @dkmeteor 也提到,Java的泛型采用了类型擦除,这样就没法保证List里会不会被塞进一个Float,因为在运行时List看到的全都是Object。因此为了防止这类事情发生,Java的泛型是不变的。
那有没有办法在Java里用上协变或者逆变的泛型呢?答案是肯定的,需要在声明里加wildcard:
但是在第一种情况中就不能呼叫参数里带有
?
的函数了,例如:这里同样是为了保证Type Safety。不能把
Float
传给Container<Double>
。同理,在逆变的时候就不能呼叫返回值带
?
的函数,除非接受返回值的变量类型是Object。部分来源:http://www.ibm.com/developerworks/cn/java/j-jtp01255.html