在读Java编程思想的时候,对泛型通配符的上下限定这块有点难以理解。其实平常我也没有用到过这个统配符,只是看着有点困惑便去网上搜了下,答案又各有说法,并且大都不是我想要的。泛型的优点就不多说了,作为一个编程语言的优秀特性,很多语言都支持泛型。在Java编码中泛型可以结合通配符来实现更多的扩展。
Java使用 "?" 在泛型参数中做通配符,作用在于:
当前你不知道指定该容器是什么类型,你可以是?占位,表明将来在使用的时候你再指明类型去使用。
主要疑惑点
Java使用通配符时候可以通过extends和super用来对将来指向容器的参数类型做限制:
向下限定: ? extends E
向上限定:? super E
我起初最疑惑的地方是编译器如何知道对应的通配类型可以存放那些数据的。
对这两个最基础的理解(结论):
< ? extends E >
add: 不允许加入
任何
元素!
get: 可以获取元素,但是必须使用
E
来接受元素!
< ? super E >
add: 允许添加
E和E的子类
元素!
get: 可以获取元素,但是类的信息丢失了,所以返回
只能使用Object引用
来接受!如果需要自己的类型需要强制类型转换!
分析前需要知道
一定谨记: 编译器使用list的时候,只知道list的泛型参数范围(?,E],至于他后面指向了什么编译器不知道,list自己是什么类型也不确定。 因为运行时候,不会再list上标记后面指向的ArrayList的类型,而是用一个capture#1来标记(原理我暂时还不知道)。
由上,你使用list的时候,编译器所能获取的信息只有一个,list的泛型参数范围:(?,E] 或者 [E,?) 所以如果允许get或者add,为了安全一定要进行类型判断,保证类型安全!这些判断的依据来源也只有该参数范围!
那么问题来了: 我只知道(?,E] 或者 [E,?)的范围,不知道后面ArrayList具体是什么?所以我做出的决策只能是规定:
只允许 E的父类 或者 E的子类 的对象添加!
(因为list不确定自己的类型,只知道自己的范围,所以它对应的可add对象的范围一定是这个泛型参数范围内无论取哪种类型都能保证add是类型安全的)。但是最终我们是要add进入ArrayList的,所以这个范围一定要ArrayList无论指定任何类型,都是类型安全的!
实例代码:
ArrayList<C> al = new ArrayList<C>()
al.add(new C())
List<? extends A> list = al
// list不允许加入任何元素
// list.add(new A())
// list.add(new B())
// list.add(new C())
// list可以获取元素,但是接受元素必须是A,是其他的会报错!
A a1 = list.get(0)
// C c1 = list.get(0)
List<? super C> list1 = new ArrayList<B>()
// list不允许加入C的父类元素
// list1.add(new A())
// list1.add(new B())
list1.add(new C())
// list获取元素需要强制类型转换(或用Object接受),因为类信息使用list会被丢失
// C c2 = list1.get(0)
C c2 = (C)list1.get(0)
复制代码
分析
< ? extends E >
List < ? extends A > 表示 这个list里面存的是A的子类,具体是啥不知道,只知道范围!
那可以设定分两个范围: A子类,A父类。
对于add:
A子类情况:如果你允许add的元素是A的子类,那么因为泛型参数限定? extends A,所以ArrayList可以指定的类型也是A的子类,那么无法保证add的对象一定ArrayList指定类型的子类对象,比如: ArrayList指定C, list却add了A()对象--这就是非法的!(因为list并不知道指定了C,它可以确定的范围就是可以add所有A的子类。)所以A子类该范围不行!
A父类情况:ArrayList指定类型肯定实在(?,A]范围内,所以这种情况肯定不行!
对于get:
list不知道你存入ArrayList是什么类型,但是我可以确定使用A的引用肯定可以接受ArrayList中的元素(无论ArrayList设定什么类型),因为A肯定是里面元素的父类!但是你使用其他类型接受就不行了,因为如果ArrayList的类型是你指定的那个类型的父类,是接受不了的。
C子类:如果允许add,就允许list来add任何C的子类元素,因为ArrayList指定的范围是C和其父类,所以ArrayList可以准确接受该假设范围的所有对象。 所以,C子类这个范围可行,这样就有一个标准的类型判断依据,不像extends,根本无法确定判断标准,编译器就不会知道怎么做。
C父类:显然和上面A子类情况是一样的!因为list无法知道ArrayList指定类型 ,所以无法确定判断标准。
其实最初我疑虑点就在为啥我不能限定他只添加ArrayList制定范围之后的那个呢?
----原因很简单,编辑器使用list的时候不知道ArrayList是啥!
后来又纳闷为啥super没毛病同样也不知道啊为啥可以add?
----因为即便是super的时候,也是不知道的,super能add完全是靠常规逻辑判断!