一.类型参数形态
按照泛型类型参数的表现形态,先列出所有可能出现的,再来归纳,以List为例:
- Holder:原生态类型
- Holder<T>:最简单的泛型形态。
- Holder<T extends Number>:有限制形式类型参数。
- Holder<T super Number>:有限制形式类型参数。可惜的是没有这种语法。
- Holder<? extends T>:子类型通配符。
- Holder<? super T>:超类型通配符。
- Holder<?>:无界限定符。
- Holder<Object>:同List。
二.有限制形式类型参数
Holder<T extends Number>,实际参数类型将只能是Number的子类型。
三.通配符
- 子类型通配符:能读不能写。我知道Holder里持有的永远是T的子类,所以get必定就是T类型;但我只知道是T的子类型,但具体是什么,没法确定,为了保证类型安全,不允许set,当然能set(null),因为null恰好不需要类型。
- 超类型通配符:能写不能读。我知道Holder里持有的永远是T的超类,那我set一个T类型的对象当然是可以的(T的子类当然也可以),当然set(null)也完全没问题;但到底是哪个超类呢,不确定,所以get操作类型是不确定的,只能get出一个Object。
- 无界通配符:不能读不能写。没有任何界限,我永远不知道用户想使用一个什么类型,get操作也只能get出一个Object,set操作只能set一个null。
- 总结:PECS produces-extends, consumer-super。produces && consumer请使用原生类型。
package com.jyz.study.jdk.generic; /** * 通配符 * @author JoyoungZhang@gmail.com * */ public class Wildcards { //原生类型 static void rawArgs(Holder holder, Object arg){ holder.get(); holder.set(arg); holder.set(new Wildcards()); } //无界通配符 static void unboundedArg(Holder<?> holder, Object arg){ Object object = holder.get(); T t = holder.get();//compile error, don't hava any T holder.set(arg);//compile error holder.set(null); } //子类型通配符 static <T> T wildSubtype(Holder<? extends T> holder, T arg){ holder.set(arg);//compile error holder.set(null); T t = holder.get(); return t; } //超类型通配符 static <T> T wildSupertype(Holder<? super T> holder, T arg){ holder.set(arg); holder.set(null); T t = holder.get();//compile error Object obj = holder.get(); return t; } class Holder<T> { private T element; public Holder() { } public Holder(T element) { this.element = element; } public T get(){ return element; } public void set(T arg){ this.element = arg; } public boolean equals(Object obj){ if(!(obj instanceof Holder)){ return false; } Holder<?> holder = (Holder<?>)obj; return element == null ? holder.element == null : element.equals(holder.element); } } }
四.四者区别
- List:原生类型,没有泛型存在,List存在的是Object类型,取出来也是Object类型,需要自己做类型转换。
- List<Object>:List实际上就是List<Object>,我觉得没必要显示的来说明一下List<Object>。
- List<?>:具有某种特定类型的非原生List,只是这个类型还不确定,将有使用者去确定。确定后,就永远存在这个类型的对象,取出来也是这个类型的变量。
- List<? extends Object>:具有某种特定类型的非原生List,这个类型也还不确定,将有使用者去确定,并且,这个类型必须是Object的子类。因为恰好Java里任何类都是Object的子类,所以我觉得List<? extends Object>意义不大,使用List<?>足够了,如果换成List<? extends Number>这个语法才能发挥威力。
五.什么使用T 什么时候使用?
- 定义一个泛型类或泛型接口时,使用<T> <T extends 具体类型>。
- 引用泛型类时,使用<?> <? extends T> <? super T>。一般是将泛型类或接口定义成一个变量(成员变量和局部变量),或将泛型类或接口作为返回值时。
- 下面这个泛型类差不多涵盖了泛型最复杂的表现形态。能轻松写出来,使用泛型对你来说就是小事了。
package com.jyz.study.jdk.generic; import java.io.IOException; import java.util.ArrayList; import java.util.List; /** * 什么时候用T 什么时候用? * @author JoyoungZhang@gmail.com * http://zy19982004.iteye.com/blog/1978028 * */ public class TOrQuestionMark<TT> { protected Holder<? extends Throwable> list1; protected Holder<? super Throwable> list2; private Holder<? extends Throwable> test1(Holder<? extends Throwable> list){ return list; } private Holder<? super Throwable> test2(Holder<? super Throwable> list){ return list; } protected List<? extends TT> list3; protected List<? super TT> list4; protected List<? extends TT> test3(List<? extends TT> list){ return list; } protected List<? super TT> test4(List<? super TT> list){ return list; } public void init() { list1 = new Holder<RuntimeException>(); list1 = new Holder<IOException>(); list2 = new Holder<Throwable>();//没办法Throwable已经没有超类了,用自己 Holder<? extends Throwable> listreturn1 = test1(new Holder<IOException>()); Holder<? super Throwable> listreturn2 = test2(new Holder<Throwable>()); //以下依赖TT,先实例化tq TOrQuestionMark<Exception> tq = new TOrQuestionMark<Exception>(); tq.list3 = new ArrayList<RuntimeException>(); tq.list3 = new ArrayList<IOException>(); tq.list4 = new ArrayList<Throwable>(); List<? extends Exception> listreturn3 = tq.test3(new ArrayList<RuntimeException>()); List<? super Exception> listreturn4 = tq.test4(new ArrayList<Throwable>()); } } class Holder<T extends Throwable>{ private T obj; public void set(T obj){ this.obj = obj; } public T get(){ return obj; } } //sorry,compile error!!!!! //class Holder2<T super IOException>{ // private T obj; // public void set(T obj){ // this.obj = obj; // } // public T get(){ // return obj; // } //}