java 泛型 extends 和 super快速记忆。
为了方便总结的更全面一点,Demo如下:
public class A {
}
public class B extends A {
}
public class C extends B {
}
这里全部以B为临街,看看上下界到底是什么情况
第一种情况,不带任何通配符,extends 和 super
ArrayList<B> Blist = new ArrayList<>();
A a = Blist.get(0);//(1)正确
B b = Blist.get(1);//(2)正确
C c = Blist.get(2);//(3)错误
Blist.add(new A());//(4)错误
Blist.add(new B());//(5)正确
Blist.add(new C());//(6)正确
结果我们也可以预料到:
B是已经确定的类型,ArrayList里面相当于有一个动态数组object[]来存储我们add()进去的数组。根据java多肽的性质,父=子才是正确的,而子=父是错误的。所以B和B的父类都可以正确接受Blist.get()得到的值,因为返回的值一定是B类型,B可以接受get()的值,那么B的父类一定也可以。同理,Blist.add()的值必须是B和B的子类,这样父=子才可以成功。
###再来看看上届extends B的情况
ArrayList<? extends B> extendsList = new ArrayList<>();
a = extendsList.get(0);//正确
b = extendsList.get(1);//正确
c = extendsList.get(2);//错误
extendsList.add(new A());//错误
extendsList.add(new B());//错误
extendsList.add(new C());//错误
因为 ? extends B,所以extendsList里面存储的是B和B的子类,所以extendsList.get()的值可以赋值给B,同理,可以赋值给B,当然可以赋值给B的父类,但是C就不行,因为C c=new B()不成立。那么为什么add的值不行呢?因为不确定,虽然保证了add()的值一定是B的子类,但是,可以为C,也可以为D(假设D也继承了B),所以所有add()全部失败。。。(姑且这么记吧)
###然后是下界super B
ArrayList<? super B> superList = new ArrayList<>();
a = superList.get(0);//错误
b = superList.get(0);//错误
c = superList.get(0);//错误
superList.add(new A());//错误
superList.add(new B());//正确
superList.add(new C());//正确
? super B,所以里面存储的是B和B的父类,既然是父类,那所有superList.get()就不成立了,因为B的父类是不确定的,可能是A,也可能是X(假设X是B的父类或者A的父类)。但是,下界是确定的,add()的值,必须为B或者B的子类,这样才符合java多态,而add(new A())显然就不行。
所以,我们想只读(只是想得到)某个T及T的子类,可以采用< ? extends T >的方式,如果想加入T及T的子类,采用< ? super T >的方式
速记:PECS
- 生产者§,生产给别人用,所以get可行,extends,get可以赋值为为B及B的父类
- 消费者©,自己消费用,所以add可行,super,可以add进去B和B的子类
额外知识点
这里需要提到一个额外知识点,就是协变和逆变。
什么叫协变呢?下面给出定义:
假设C extends B,即C <= B 存在某种关系F,满足F© <= F(B),那么,就叫做协变,否则,满足F(B)<=F©,就叫做协变。还有一种叫做两种都不满足,就叫做不变。那么,extend和super分别满足什么呢?
由上面的知识点可以知道,super其实是逆变,而extends其实是协变。
方法参数是逆变的,而返回值是协变的。
这里额外给出一个知识点
class B {
}
class A extends B {
}
class SetA extends SetB {
void set(A a) {
System.out.println("set A");
}
}
class SetB {
void set(B b) {
System.out.println("set B");
}
}
public void test() {
SetB set = new SetA();
set.set(new B());
set.set(new A());
// SetA setA = new SetA();
// setA.set(new B());
// setA.set(new A());
}
这里,结果是什么呢?
如果答案很简单,那么楼主就不会提出来了。答案是set A,set B。
那么,为什么呢?
set不明明是SetA类型吗,为什么调用set(new A())最后走的却是SetB的set(B b)方法呢?
这里留个悬念,大家自己思考下,以后我再来补充原因。