剖析Java泛型通配符

《剖析Java泛型通配符》

Iteye的编辑器,是我见过最垃圾的编辑器

 

使用泛型的目的有2个,首先是保证了安全性,其次是表述性方面的优势,毕竟对于一些语义上的错误(比如:类型转换),应该尽可能在编译期就发现。

 

泛型相信大家都不会陌生,我们在Java5后使用集合API时,基本上时时刻刻都是在和泛型打着交道,那么笔者便不再对泛型的一些基础语法进行介绍,直接进入本章主题,泛型的无限制通配符。

 

当我们在使用泛型的时候,如果不明确实际的类型参数,那么就可是使用<?>来代替,这种方式便称作无限制通配符,比如List<?>就意味着其类型参数可以是任意类型。值得注意的是,List<?>由于无法在编译期确定究竟是什么类型,因此List不能添加任何元素(除了无意义的null外),以及get操作时返回的是Object,因为编译器无法预知你需要获取的究竟是什么鬼。或许有同学会有疑问,这似乎和Object是一个尿性,那么为啥不用Object直接替代无限制通配符?这是因为实现泛型之间的父子关系,需要有一种表示未知类型的方式。

 

换句话说,使用List<?>的意义其实并不是很大,因为不能作为消费者(添加数据),获取数据时甚至还会丢失一些类型信息,比如:

List<?> list = new ArrayList<String>();

 

上述程序实例中,ArrayList<String>实际上是List<?>的派生类型,但是当我们使用get获取数据时,有可能会丢失一些类型信息,毕竟之前笔者曾经提及过,<?>由于无法确定其类型,因此返回的只能是Object。如果你无法接受,那么你可以考虑有限制的通配符类型。毕竟,相对于类型的一无所知,至少我可以明确需要满足什么样的条件,这对于后续操作起到了较大的帮助。

 

有限制的通配符,可以分为2种:

1、超类限定:<? super E>;

2、派生限定:<? extends E>。

 

如果使用超类限定,既List<? super E>,那么也就限制了类型参数只能够是E本身或者是E的超类,如下所示:

List<? super Integer> list = new ArrayList<Number>();

list.add(1);

/* 由于编译器无法确定其类型,所以返回为Object类型 */

list.get(0);

 

超类限定在操作上存在以下一些局限性:

1、只能做添加操作,类型参数只能够是其本身或者超类;

2、获取数据时,由于编译器并不明确其类型,所以返回的数据类型只能是Object。

 

接下来看看派生限定(子类限定),即List<? extends E>,那么也就限制了类型参数只能够是E本身或者是E的派生类,如下所示:

List<? extends Number> list = new ArrayList<Integer>();

/* 编译器无法确定其类型,所以无法执行添加操作 */

//list.add(1);

 

list.get(0);

派生限定在操作上存在以下一些局限性:

1、只能做获取操作,类型参数只能够是其本身或者派生类;

2、添加数据时,由于编译器并不明确其类型,所以不能够执行添加操作。

 

当然,你可能会产生疑问,有限制的通配符应该具体应用在哪一种场景下呢?参考PESC(Producer Extends,Consumer Super”)原则,当作为生产者的时候(获取操作)时,应该使用派生限定:<? extends E>,因此你不能添加数据;当作为消费者的时候(添加操作),应该使用超类限定:<? super E>;既是消费者,同时又是生产者的时候,你就不应该再继续使用泛型通配符类型

 

猜你喜欢

转载自gao-xianglong.iteye.com/blog/2398678