java 泛型 extends 和 super快速记忆。

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)方法呢?
这里留个悬念,大家自己思考下,以后我再来补充原因。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值