Java泛型
泛型
取得泛型的类型
public class SchoolLesson // 课程 <T extends Teacher, // 老师 S extends Student, // 学生 G extends Department<T,S>> // 所在的系 { private Class<T> teacherType; private Class<S> studentType; private Class<D> departmentType; @SuppressWarnings("unchecked") public SchoolLesson() { ParameterizedType parameterizedType = // (ParameterizedType) this.getClass().getGenericSuperclass(); Type[] typeArguments = parameterizedType.getActualTypeArguments(); this.teacherType = (Class<R>) resultTypePam[0]; // 不是泛型类 this.studentType = (Class<R>) resultTypePam[1]; // 不是泛型类 this.departmentType = (Class<R>) ((ParameterizedType) resultTypePam[2]).getRawType(); } }
泛型的静态方法
public final class RegistryBuilder<I> { public static <I> RegistryBuilder<I> create() { return new RegistryBuilder<I>(); } RegistryBuilder() { super(); this.items = new HashMap<String, I>(); } }
PECS
PECS指「Producer Extends,Consumer Super」。换句话说:
-
如果你是想遍历collection,并对每一项元素操作时,此时这个集合时生产者(生产
元素),应该使用
Collection<? extends T>
。 -
如果你是想添加元素到collection中去,那么此时集合时消费者(消费元素)应该使用
Collection<? super T>
可能你还不明白,不过没关系,接着往下看好了。
例子:实现一个栈
下面是一个简单的Stack的API接口:
public abstract class MyStack<E> { abstract public boolean isEmpty(); abstract public void push(E e); abstract public E pop(); }
生产者:入栈方法的形参
假设想增加一个方法,按顺序将一系列元素全部放入Stack中,你可能想到的实现方式如下 :
public void pushAll(Collection<E> src) { for (E e: src) this.push(e); }
假设有个Stack<Number>
,想要灵活的处理Integer,Long等Number的子类型的「集合」:
Stack<Number> numberStack = new Stack<Number>(); Iterable<Integer> integers = ....; numberStack.pushAll(integers);
此时代码编译无法通过,因为对于类型Number和Integer来说,虽然后者是Number的子类,
但是对于任意Number集合(如List<Number>
)不是Integer集合(如List<Integer>
)
的超类,因为泛型是不可变的。
幸好java提供了一种叫有限通配符的参数化类型,pushAll
参数替换为:
「E的某个子类型的Iterable接口」:
public void pushAll(Collection<? extends E> src) { for (E e: src) this.push(e); }
这样就可以正确编译了,这里的<? extends E>
就是所谓的producer-extends
。这里的
Iterable就是生产者,Iterable<? extends E>
可以容纳任何E的子类。在执行操作时,
可迭代对象的每个元素都可以当作是E
来操作。
消费者:出栈方法中的形参
与之对应的是:假设有一个方法popAll()
方法,从Stack集合中弹出每个元素,添加到
指定集合中去。
public void popAll(Collection<E> dst) { while (!this.isEmpty()) dst.add(this.pop()); }
假设有一个Stack<Number>
和Collection<Object>
对象:
Stack<Number> numberStack = new Stack<Number>(); Collection<Object> objects = ...; numberStack.popAll(objects);
同样上面这段代码也无法通过,解决的办法就是使用Collection<? super E>
。这里的
objects
是消费者,因为是添加元素到objects
集合中去。
使用Collection<? super E>
后,无论objects
是什么类型的集合,满足一点的是他是
E
的超类,所以不管这个参数化类型具体是什么类型都能将E
装进objects
集合中去。
public void popAll(Collection<? super E> dst) { while (!this.isEmpty()) dst.add(this.pop()); }
总结
-
如果你是想遍历collection,并对每一项元素操作时,此时这个集合时生产者(生产
元素),应该使用
Collection<? extends Thing>
。 -
如果你是想添加元素到collection中去,那么此时集合时消费者(消费元素)应该使用
Collection<? super Thing>
注:此文根据《Effective Java》以及Java Generics: What is PECS? 整理成文。想了解 更多有关泛型相关知识,请读者阅读《Effective Java》的第五章。