1. 泛型类。在类名后面声明泛型变量。
class GenericMap<T extends Number, E extends Date> extends HashMap<T, List<E>> {
....
}
new GenericMap<Integer, Time>()的时候才最终绑定T(Integer)和E(Time)。所以这个map的get方法实际上变成了
public List<Time> get(Integer key) {
return super.get(key);
}
2. 泛型方法。在返回值之前申明泛型变量。
public static <T, E extends Number> int getSize(Map<T, List<E>> map) {
return map.size();
}
综上所述,泛型变量只能在类名后面或方法的返回值前面声明。注意泛型变量可以不加任何约束<T>,或者只能用上限<T extends Number>,不能用下限,后面将提到。
3. Java的泛型并不能很好的支持多态
Example: List<Number> list = new ArrayList<Integer>(); // 编译错误
Example:
public static void testGeneric1(List<Number> list) {
// ...
}
...
testGeneric1(new ArrayList<Integer>()); // 编译错误
...
这时候必须用List<? extends Number>来代替List<Number>。这就要使用通配符(?)了
4. wildcast泛型通配符? 泛型上限和下限 bound
上面例子提到用<? extends Number>来解决编译错误,这是用到了泛型的上限,同理还可以定义下限,比如<? super Number>.
上边界限定通配符 <? extends T>,T是类型参数上界
public void upperBound1(List<? extends Number> list) {
Number number = list.get(0); // 可见上限主要是用于从容器中往外取元素的,取出的内容都可视为其上限类型T
list.add(number); // 这句话无法通过编译,因为在运行时,传入的参数可能是List<Integer>
}
如果非要写的话,所以这个方法可以这么写
public <T extends Number> void upperBound2(List<T> list) {
Number number = list.get(0);
list.add((T)number); // 除非作强制类型转换,否则父类型是无法放入字类型容器中的。
}
下边界限定通配符 <? super T>,T是类型参数的下界
public void lowerBound1(List<? super Number> list) {
Number number = list.get(0); // 编译错误。因为在运行时,传入的参数可能是List<Object>,须作强制类型转换
list.add(number); // 可见下限主要是用于往容器中放 下限类型T 的对象
}
无边界通配符<?>, 不做任何限制
不能读取(读出的类型为?,因此无法使用),也不能写入,只能用跟通配类型无关的一些API
public void unBound(List<?> list) {
list.size();
}
PECS原则:
如果要从集合中读取类型T的数据,并且不能写入,可以使用 ? extends 通配符;(Producer Extends)
如果要从集合中写入类型T的数据,并且不需要读取,可以使用 ? super 通配符;(Consumer Super)
public <T> void copy(List<? super T> dest, List<? extends T> src)
{
for (int i=0; i<src.size(); i++) {
dest.set(i,src.get(i));
}
}
综上所述,泛型通配符只能用在变量声明(定义)或方法的形参声明上。泛型通配符可以不加任何约束<?>,或者用上限<? extends T>或下限<? super T>。
5. <?>和<Object>的区别
List<?> list 表示 list 是持有某种特定类型的 List,但是不知道具体是哪种类型。那么我们可以向其中添加对象吗?当然不可以,因为并不知道实际是哪种类型,所以不能添加任何类型,这是不安全的。而单独的 List list ,也就是没有传入泛型参数,表示这个 list 持有的元素的类型是 Object,因此可以添加任何类型的对象,只不过编译器会有警告信息。