什么是泛型:
Generic programming means to write code that can be reused for objects of many different types. 摘自《 Core java 》
为什么使用泛型
主要是为了类型转换。如果不使用泛型,那么每次都得显式的就行类型转换。
如:
不使用泛型:
List list = …
String str = (String)list.get(1);
使用泛型:
List<String> list = …
String str = list.get(1);
如何定义泛型:
使用 <Type variable> 中带有类型,该类型可以是具体类型 . 比如 : String.
也可以不是具体类型,比如 <? Extends Number>. 具体类型 (reifiable type) 和非具体类型 (unreifiable type)
(非)具体化类型后面再介绍
< Type variable > 即表明声明了泛型,可以在类、方法、字段等处定义泛型。
什么是类型参数( type parameter ) 、 参数化类型( parameterized type )
比如,类 List<String> ,称 String 为类型参数。 List<String> 为参数化类型,因为 List 被 String 参数化了。
子类型( subtypes )及替换原则
Integer 是 Number 的子类,能放置 Number 的地方都能放置 Integer ,这便是 Java 中的替换原则。
Substitution Principle: a variable of a given type may be assigned a value of any subtype of that type, and a method with a parameter of a given type may be invoked with an argument of any subtype of that type 。 -- 摘自 Java Generics & Collections
数组 Integer[] 是 Number[] 的子类,那是不是 List<Integer> 也是 List<Number> 的子类?其实不是,他们其实是同一类型。可以通过以下代码判断。
List<Number> numList = …
List<Integer> intList = …
System.out.println(numList.getClass() == intList.getClass())
如果我们需要在泛型中拥有类似于数组的该特性( Integer[] 是 Number[] 的子类),我们就得使用通配符 (wildcard).
通配符 (wildcard) 及 访问原则
上述说过,数组 Integer[] 是 Number[] 的子类, List<Integer> 确不是 List<Number> 的子类。要实现类似于数组的该特性,就需要通配符来实现 : List< ? extends Number> .
? 表示通配符,表示任意类型(继承 Object 的任意类型)。
? extends T 表示继承或实现 T 的类型。即 T 的任意子类型 , 比如 ? extends Number 表示继承自 Number 的任意类型,可以是 Integer, Double …
? super T 表示 T 的任意父类型( super type ),比如: ? Super ArrayList 表示 ArrayList 的任意父类型,可以是 List, AbstractList… 。
? 表示 ? extends Object
通过通配符,我们可以实现类似于数组的功能:
List<Integer> intList = …;
List<Number> numList=…;
List<? Extends Number> list1 = intList;
List<? Extends Number> list2 = numList;
List<? Super Number> list3 = numList;
这样我们可以向数组一样进行类型转换。但却不能像数组一样进行以下方式的赋值:
Number[] nums = …
Nums[0] = Integer.valueOf(2)..
如果编写以下代码:
List<? Extends Number> list = … ;
List.add(Integer.valueOf(2)) // 编译器报错。
List.add(Double.valueOf(2)) // 编译器报错
为什么呢? <? Extends Number> 不是表示 Number 的任意子类型么? 使用替换原则不是应该正确的么?
该问题的源头就是因为 <? Extends Number> 表示 Number 的子类型。但是我们却不知道到底是哪个具体的类型。因此 List< ? extends Number> 可以指向其任意的子类型。比如, List<Integer>, List<Number>,List<Double>. 如果是 list 是指向 ArrayList<Double>, list.add(Integer.valueOf(2)) 合法的话,那么 list 中就存在 Double 、 Integer 等 Number 子类型的对象, ArrayList<Double> 中的元素类型就不一样。那么 list.get(..) 就有可能返回 Integer 类,就可能出现问题,由于泛型有个基本原则:
Cast-iron guarantee:
the implicit casts added by the compilation of generics never fail.
-
-
-
-
-- 摘自 Java Generics & Collections
-
-
-
所以编译器在编译级别就将可能导致类型转化的错误屏蔽掉。
当然我们可以调用 list.add(null), 因为 null 是任意类型的子类型,可以安全的使用,但很多时候没有用处。
如果使用 list.get(..) 就不会有问题,因为 list 存的就是 Number 子类对象,任何子类都能转为其基类( Number )。
同样的道理,以下代码无法编译:
List<? Super Integer> list = …
Integer in = list.get(0); // 编译过不去
因为 List 存的是 Integer 的基类型,但是我们并不知道具体是哪个基类。所以就无法进行转化 , 当然我们可以调用 Object obj = list.get(0) 。
如果使用 list.add(Integer.valueOf(2)) 却不会有问题,因为 list 存的是 Integer 的父类,那么必然可以存 Integer 类型。
因此利用 < ? extend …> <? Super …> 时,一般使用一个原则:
The Get and Put Principle: use an extends wildcard when you only get values out of a structure, use a super wildcard when you only put values into a structure, and don't use a wildcard when you both get and put.
-
-
-
-
-- 摘自 Java Generics & Collections
-
-
-
类型参数 VS 通配符
比如我们需要将一个list的数据拷贝到另一个list中。
假设方法在类Collections中定义:
public static <T> void copy(List<? super T> dst, List<? extends T> src)
List<Object> objs = Arrays.<Object>asList(2, 3.14, "four");
List<Integer> ints = Arrays.asList(5, 6);
List<Object> objs = Arrays.<Object>asList(2, 3.14, "four");
List<Integer> ints = Arrays.asList(5, 6);
Collections.copy(objs, ints);
assert objs.toString().equals("[5, 6, four]");
由以下方法调用, <Object> 表示 T 为 Object
Collections.copy(objs, ints);
Collections.<Object>copy(objs, ints);
Collections.<Number>copy(objs, ints);
Collections.<Integer>copy(objs, ints);
比较以下几种方法签名:
1:public static <T> void copy(List<T> dst, List<T> src)
2:public static <T> void copy(List<T> dst, List<? extends T> src)
3:public static <T> void copy(List<? super T> dst, List<T> src)
4:public static <T> void copy(List<? super T> dst, List<? extends T> src)
比较:
1 :限制最大,必须要求 dst 和 src 是同一类型,比如同是 List<Integer>.
该方法不能用于 上述的例子 collectioins.copy(objs,ints)
2: 仅仅使用于T为Object时。即Collections.<Object>copy(objs, ints)方式
3:仅仅使用于T为Integer时,Collections.<Integer>copy(objs, ints);
4 :可应用于 T 为 Object 、 Number 、 Integer 时。
以上例子摘自 – Java Generic & Collections
简单的总结一下:
泛型方法签名尽可能的使用 wildcard ,其可用范围更广。如果需要严格限制为同一类型时,就可以使用 type parameter 。
Wildcard capture
译为 捕捉通配符??? 何为 wildcard capture. 顾名思义 : 就是捕捉通配符的类型。 看以下例子:
Pair<T>有first 和second两个变量。分别有对应的set, get 方法。
public static void swap(Pair<?> p){
? t = p.getFirst(); // ERROR
p.setFirst(p.getSecond());
p.setSecond(t);
}
但是用T确实没有问题:
public static <T> void swapHelper(Pair<T> p)
{
T t = p.getFirst();
p.setFirst(p.getSecond());
p.setSecond(t);
}
因此我们经常使用swapHelper(Pair<T> p)来实现swapHelper(Pair<?> p):
public static void swap(Pair<?> p) { swapHelper(p); }
这种情况,我们叫T 捕捉了 通配符(T captures the wildcard).我们并不知道wildcard表示什么类型,但是我们知道其必然是一个明确的类型。因此可以用T去捕捉它。(这里有点绕,知道T可以捕捉?即可)。
注意的是:要用T捕捉?,那么要保证wildcard必须是表示同一、明确的类型(尽管我们不知道这是什么类型)。
比如 ArrayList<List<T>>就不能用来捕捉ArrayList<List<?>>,因为ArrayList可能包含两个或以上List<?>,每个List<?>拥有不同的类型。
以上例子摘自—Core Java
擦除( erasure )
泛型定义:
泛型方法:
Bridge Method
具体化类型 (reifiable type)
泛型对象创建
消除 Unchecked warning
反射与泛型
Effective generic