Java泛型边界问题,super关键字
Posted linlei2099
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java泛型边界问题,super关键字相关的知识,希望对你有一定的参考价值。
背景
- Java给定一个具体的类型参数A之后的泛型List,与给定另一个具体的类型参数X的泛型List
- super或者extends可以定义一大类的泛型,作为给出具体类型参数的泛型的父类。
- super或者extends定义的有边界泛型,根据参数类型的层次覆盖判定和具体参数类型泛型之间的层次关系。
不要对super望文生义
super关键字,用于类的方法中表示指向父类对象的引用。
在泛型边界语法中指出泛型下界。
假设有继承关系,A <- B <- C <- D <- E
void f( List<? super C> param){...}
List<? super C>
是List<B>
,List<A>
,List<C>
,List<Object>
或者List<? super B>
,List<? super A>
的父类,对于接受List<? super C>
为参数的方法f
中,传递上述类型时,可以隐式向上转型,安全。在方法中,取出的item都是Object类型,List<? super C>
取出的类型都是Object,不是C或者其他的具体类。
如果需要cast取出的item为具体类型,程序员自己保证存在List中的对象都是可以安全强制转型的。最好的应用场景是不需要针对具体类型的item进行处理,其次是程序员自己保证所有item可以安全强制转型。
方法f
无法接受List<D>
或List<E>
,cast强制转型也无法通过编译,Java不接受没有层次关系的两个类型的强制转型。这是泛型规则规定的:它们和List<B>
,List<A>
,List<C>
没有父子关系。
假设方法内
List<? super C> param;
param.add(new B());// ERROR
param.add(new Object());//ERROR
param.add(new D());//right
List<? super C>
存入item的限制很大而且看起来有点奇怪:任何C的父类和C类型的对象都不能存入,C的子类可以存入。看起来和前一节所规定的List<...>
父子关系相矛盾。
先说结论:super限制的泛型不适合存,只适合消费它
PECS(producer extends,Consumer super)规则。
- 为什么List<? super C> param取出来的是Object,而不是具体类型?
因为在声明param的时候只要求这个param是List<? super C>,翻译成人话就是声明param是List<B>
,List<A>
,List<C>
,List<Object>
或者是List<? super B>
,List<? super A>
中的一种,不能保证是具体的哪一种,所以编译器不知道取出的item是哪一种具体类型。如果程序员自己能够保证传入的是同一种具体类型的item,那么自行强制转型,但是编译器只能简单地约定从List<? super C>
中取出的Object类型。
- 为什么不能向
List<? super C>
存入A B Object类型,而可以存入C和D E?
因为存入A B或者Object类型可能会破坏原来List的类型一致性,正是为了避免这个问题才在JDK5引入泛型特性的,因此不能允许。而存入C的子类时,通过隐式转换为C可以保证不因此破坏List中item类型的一致。
- 既然不能保证取出的item是同一种具体类型,要super有何用?
super没有限制死f
接受的是哪一种具体的List<..>,同时也做出了一定的限制,不能是List<B>
,List<A>
,List<C>
,List<Object>
或者List<? super B>
,List<? super A>
之外的List。如果传递一个没有边界的List<?>
强制转型那就是另外的事情了。
场景(由于某些原因,子类DE的item list不需要被f处理):
void f( List<? super C> param){
if(param.get(0) instanceof A){
for(Object item : param){
A a = (A)item;
a.doAsA();
}
} else(param.get(0) instanceof B){
for(Object item : param){
B b = (B)item;
b.doAsB();
}
}
}
上面的场景代码还是有点牵强,没有想到实际的应用场景。这可能也是关键字super限制泛型下界这个java特性的不足吧,但是在extends之外多提供一种选择,也不算坏事。
extends
PECS规则指出extends用于生产item到泛型的时候。
List<? extends C> void f(){
List<? extends C> ret = getNewList();
for(int i =0; i<10; i++){
ret.add(new C());
ret.add(new D());
}
return ret;
}
调用f
的代码可以获得一个List,编译器确保该List中的item都是C类型。比较好理解,看起来extends更强大,有没有使用super比使用extends来限定泛型边界的更好的场景?
大概就是当上界不需要限制的时候,这时如果使用<? extends Object>,那么就相当于没有限制。
所以super不是完全可以被extends替代的,多一种选择更好。
以上是关于Java泛型边界问题,super关键字的主要内容,如果未能解决你的问题,请参考以下文章