泛型即编写模板代码来适应任意类型。使用时不必对类型进行强制转换,并通过编译器对类型进行检查;
例如,编一个Pair
类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class Pair <T , K > { private T first; private K last; public Pair (T first, K last) { this .first = first; this .last = last; } public T getFirst () { ... } public K getLast () { ... } public static <Q> Pair<Q> create (Q first, Q last) { return new Pair<Q>(first, last); } }
如上,需要注意,静态泛型方法不能引用泛型类型,应该使用其他类型区分。
擦拭法 不同语言的泛型实现方式不一定相同。Java语言的泛型实现方式是擦拭法(Type Erasure)。
即,虚拟机对泛型一无所知,所有的工作由编译器完成:
编译器内部把类型<T>
视为Object
处理。
需要转型时,编译器根据<T>
的类型自动实现安全的强制转型。
因此,这也带来了几点局限性:
<T>
不能是基本类型,例如int
,因为实际类型是Object
,Object
类型无法持有基本类型
所有泛型实例,无论T
的类型是什么,getClass()
返回同一个Class
实例,因为编译后它们全部都是Pair<Object>
。
因此,if (p instanceof Pair<String>)
也会编译错误。
不能实例化T
类型。即new T();
会编译错误。正确的实例化:
1 2 3 4 5 6 7 public Pair (Class<T> clazz) { first = clazz.newInstance(); last = clazz.newInstance(); } Pair<String> pair = new Pair<>(String.class);
泛型继承 在继承了泛型类型的情况下,子类可以获取父类的泛型类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 import java.lang.reflect.ParameterizedType;import java.lang.reflect.Type;public class Main { public static void main (String[] args) { Class<IntPair> clazz = IntPair.class; Type t = clazz.getGenericSuperclass(); if (t instanceof ParameterizedType) { ParameterizedType pt = (ParameterizedType) t; Type[] types = pt.getActualTypeArguments(); Type firstType = types[0 ]; Class<?> typeClass = (Class<?>) firstType; System.out.println(typeClass); } } } class Pair <T > { private T first; private T last; public Pair (T first, T last) { this .first = first; this .last = last; } public T getFirst () { return first; } public T getLast () { return last; } } class IntPair extends Pair <Integer > { public IntPair (Integer first, Integer last) { super (first, last); } }
extends通配符 使用类似<? extends Number>
通配符作为方法参数时表示:
方法内部可以调用获取Number
引用的方法,例如:Number n = obj.getFirst();
;
方法内部无法调用传入Number
引用的方法(null
除外),例如:obj.setFirst(Number n);
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public class Main { public static void main (String[] args) { Pair<Integer> p = new Pair<>(123 , 456 ); int n = add(p); System.out.println(n); } static int add (Pair<? extends Number> p) { Number first = p.getFirst(); p.setFirst(new Integer(first.intValue() + 100 )); return p.getFirst().intValue() + p.getFirst().intValue(); } } class Pair <T > { ... public T getFirst () { return first; } ... public void setFirst (T first) { this .first = first; } ... }
因此,许多方法参数用到此种上界通配符,如List<? extends Integer>
,通常也代表着该方法内部只会读取List
的元素,不会修改List
的元素。
使用类似<T extends Number>
定义泛型类时表示:
泛型类型限定为Number
以及Number
的子类。
super通配符 Pair<? super Integer>
表示,方法参数接受所有泛型类型为Integer
或Integer
父类的Pair
类型
使用下界通配符作为方法参数时表示:
方法内部可以调用传入Integer
引用的方法,例如:obj.setFirst(Integer n);
;
方法内部无法调用获取Integer
引用的方法(Object
除外),例如:Integer n = obj.getFirst();
。
即使用super
通配符表示只能写不能读。
Java标准库的Collections
类定义的copy()
方法:
1 2 3 4 5 6 7 8 9 public class Collections { public static <T> void copy (List<? super T> dest, List<? extends T> src) { for (int i=0 ; i<src.size(); i++) { T t = src.get(i); dest.add(t); } } }
方法的定义就展示了extends
和super
的意图:方法内部不会读取dest
,也不会修改src
。
无限定通配符 既不能读,也不能写,只能做一些null
判断:
1 2 3 static boolean isNull (Pair<?> p) { return p.getFirst() == null || p.getLast() == null ; }
此外,Pair<?>
是所有Pair<T>
的超类:
1 2 Pair<Integer> p = new Pair<>(123 , 456 ); Pair<?> p2 = p;