这里主要针对一些容易混淆的点:
泛型方法:
为了在静态(static)方法中使用泛型类的类型参数(“T”),于是有了泛型方法,注意泛型方法的格式,类型参数<T>需要放在函数返回值之前。
static <T> void fromArrayToCollection(T[] a, Collection<T>c){
边界通配符:
通配符解决使用(注意,这里是使用)现有泛型容器(类)时无法事先确定类型的情况。
由于泛型的不可协变的特性,导致
List<Object> list = new ArrayList<String>();这样的语句是不能通过编译的。(泛型的不可协变性是为了类型安全考虑的,如果List<Object>可以引用new ArrayList<String>()那么势必在List<Object>中也可以添加Integer对象,最后导致一个ArrayList<String>中存放了Integer对象,造成类型安全问题)
于是我们需要借助边界通配符来解决这个问题,List<? extends Object> oList =new ArrayList<String>();这样就可以通过编译了。
另外,在泛型方法的入参里也经常会用到通配符。
public void testMethod1(List<?extends T> param) {}
表示接受一个以T或者T的子类为类型参数的List变量。
这里有一个局限,就是这样声明的param不能进行添加(add)操作,因为“?”表示一个不确定的类型,所以它不能接受任何类型的元素,不过null元素是个例外。
public void testMethod2(List<?super T> param){}
表示接受一个以T或者是T的父类为类型参数的List变量。
这里你就可以向param其中添加T或者T的父类的实例。不过这个方法中你调用param.get()方法获取其中的对象得到的将永远是Object类型
一句话总结:
如果你想从一个数据类型里获取数据,使用 ? extends 通配符
如果你想把对象写入一个数据结构里,使用 ? super 通配符
如果你既想存,又想取,那就别用通配符。
类型擦除:
类型擦除指的是通过类型参数合并,将泛型类型实例关联到同一份字节码上。编译器只为泛型类型生成一份字节码,并将其实例关联到这份字节码上,因此泛型类型中的静态变量是所有实例共享的。
此外,需要注意的是,一个static方法,无法访问泛型类的类型参数,因为类还没有实例化,所以,若static方法需要使用泛型能力,必须使其成为泛型方法。
类型擦除的关键在于从泛型类型中清除类型参数的相关信息,并且再必要的时候添加类型检查和类型转换的方法。在使用泛型时,任何具体的类型都被擦除,唯一知道的是你在使用一个对象。比如:List<String>和List<Integer>在运行事实上是相同的类型。他们都被擦除成他们的原生类型,即List。