泛型,继承和子类型,PECS

3 篇文章 0 订阅
1 篇文章 0 订阅

1.协变

Apple extends Fruit
Apple[] 父类是 Fruit[] (数组的协变)

给定两种更具体的类型A和B(例如Fruit和Apple)
无论A和B是否相关
MyClass<A> 与MyClass<B> 都没半毛钱关系
他们的公共父对象是Object

只要T不变以下继承关系就存在

B extends A
C extends B 
D exstends C

那么B<Apple> 继承自A<Apple>
那么C<Apple> 继承自B<Apple>
那么D<Apple> 继承自C<Apple>

2. <? extends Fruit >

Apple extends Fruit
Banana extends Fruit
RApple extends Apple
GApple extends Apple
Plate<? extends Fruit>

上界通配符覆盖的区域,表达的意思是一个能放水果以及一切是水果派生类的盘子Plate<? extends Fruit>是Plate<Fruit>以及Plate<Apple>的基类,直接的好处就是我们可以用苹果盘子给水果盘子赋值了

转成<? extends Fruit >后的后遗症:
不能放也不能拿,拿的时候只能用水果和Object(能读不能写)
set不能放,但可以通过反射放入,反射就破坏了泛型,安全没法保证

3. <? super Fruit >

Fruit extends Food

Plate<? super Fruit>
下界通配符覆盖区域,表示意思就是一个能放水果以及一切水果基类的盘子,Plate<? super Fruit>是Plate<Fruit>的基类,但不是Plate<Apple>的基类

转成<? super Fruit >后的后遗症:
可以把Plate<Fruit>以及它的基类Plate<Food>转换成Plate<? super Fruit>它可以存数据(能写不能读),
但是取出来后泛型信息丢失了,只能用Object存放

public static void scene05(){
        Plate<? super Fruit> lowerfruitPlate = new Plate2<Food>();
        lowerfruitPlate.set(new Apple());
        lowerfruitPlate.set(new Banana());

        //Fruit newFruit = lowerfruitPlate.get(); //错误
        //Apple newFruit2 = lowerfruitPlate.get();//错误
        Object newFruit3 = lowerfruitPlate.get();//ok
    }

4. Plate<?>

非限定通配符,告诉这个是一个泛型,但是泛型信息类型是未知的,等价于Plate<? extends Object>

以下两个统称为限定通配符

Plate<? extends T> //上界
Plate<? super T> //下界

通配符让泛型转型更灵活,泛型T一定是具体的某一种泛型类型,泛型类型之间转换会有问题(水果盘子里装香蕉和苹果的问题)。

非限定通配符的副作用: 既不能读也不能写
作用:保证检查类型
List 不会安全检查
List<?> 进行安全检查

5. Java泛型PECS原则

PECS 即Producer extends Consumer super,提升了API的灵活性
如果你只需要从集合中获得类型T,使用<? extends T>通配符
如果你只需要将类型T放到集合中,使用<? super T>通配符
如果你既要获取又要放置元素,则不使用任何通配符。例如List<Apple>
<?>既不能存也不能取

例子(copy方法的演化)
1.两个苹果盘子放苹果把id为1的copy到id为2的苹果上

public static void scene07() {
        List<Apple> src = new ArrayList<>();
        src.add(new Apple(1));
        List<Apple> dest = new ArrayList<>(10);
        dest.add(new Apple(2));
        System.out.println(dest);
        copy(dest,src);
        System.out.println(dest);
    }
    public static void copy(List<Apple> dest, List<Apple> src){
        Collections.copy(dest,src);
    }

结果:

[Apple[id=2]]
[Apple[id=1]]

2.如果改成对两个香蕉盘子放香蕉实现copy方法那么就不能调用copy方法了,所以使用泛型改进copy方法为copy1。

public static void scene07() {

        List<Banana> src1 = new ArrayList<>();
        src1.add(new Banana(1));
        List<Banana> dest1 = new ArrayList<>(10);
        dest1.add(new Banana(2));
        System.out.println(dest1);
        copy1(dest1,src1);
        System.out.println(dest1);
    }
    public static <T> void copy1(List<T> dest, List<T> src){
        Collections.copy(dest,src);
    }

结果:

[Banana[id=2]]
[Banana[id=1]]

3.如果改成对一个水果盘子放香蕉和一个香蕉盘子放香蕉实现copy方法那么就不能调用copy1了,所以使用改进copy1方法为copy2。

public static void scene07() {

        List<Banana> src2 = new ArrayList<>();
        src2.add(new Banana(1));
        List<Fruit> dest2 = new ArrayList<>(10);
        dest2.add(new Banana(2));
        System.out.println(dest2);
        Test.<Banana>copy2(dest2,src2);
        System.out.println(dest2);
    }
    public static <T> void copy2(List<? super T> dest, List<T> src){
        Collections.copy(dest,src);
    }

结果:

[Banana[id=2]]
[Banana[id=1]]

4.Test.<Banana>copy2(dest2,src2);改成Test.<Fruit>copy2(dest2,src2);实现copy方法那么就不能调用copy2了,所以使用改进copy2方法为copy3。

public static void scene07() {

        List<Banana> src2 = new ArrayList<>();
        src2.add(new Banana(1));
        List<Fruit> dest2 = new ArrayList<>(10);
        dest2.add(new Banana(2));
        System.out.println(dest2);
        Test.<Fruit>copy3(dest2,src2);
        System.out.println(dest2);
    }
    public static <T> void copy3(List<? super T> dest, List<? extends T> src){
        Collections.copy(dest,src);
    }

结果:

[Banana[id=2]]
[Banana[id=1]]

copy3也是最终版的copy

// An highlighted block
public static <T> void copy3(List<? super T> dest, List<? extends T> src){
        Collections.copy(dest,src);
    }

src是生产者,可以从里面取数据(读),只要是T的子类型就可以传参
dest是消费者,可以把里面写数据(写),只要是T的父类型也可以往里面存

使用通配符就一个目的,为了灵活转型(API中有很多用)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值