前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[简单粗暴]一文彻底搞懂Java泛型中的PECS原则(在坑里躺了多年终于爬出来了)

[简单粗暴]一文彻底搞懂Java泛型中的PECS原则(在坑里躺了多年终于爬出来了)

作者头像
九转成圣
发布2024-04-10 17:02:28
1130
发布2024-04-10 17:02:28
举报
文章被收录于专栏:csdncsdn

[简单粗暴]一文彻底搞懂Java泛型中的PECS原则(在坑里躺了多年终于爬出来了)

两种限定通配符

  1. 表示类型的上界,格式为:<? extends T>,即类型必须为T类型或者T子类
  2. 表示类型的下界,格式为:<? super T>,即类型必须为T类型或者T的父类

PECS原则

生产者只能用extends,消费者只能用super

上代码

代码语言:javascript
复制
public class Fruit {
}
代码语言:javascript
复制
public class Apple extends Fruit{
}
代码语言:javascript
复制
public class Banana extends Fruit{
}

List<? extends Fruit> list 的理解

正如表面意思<? extends Fruit>表示的是泛型的类型是Fruit或者Fruit的子类,也就是说我们给list赋值时泛型可以写成Fruit或者Fruit的子类,可以是 new ArrayList(),也可以是new ArrayList(),还可以是new ArrayList()

看有些地方说"<? extends Fruit>表示list中的元素可能是Fruit或者Fruit的子类",感觉这种说法很有问题,

代码语言:javascript
复制
private static List<? extends Fruit> getExtendsList() {
    List<? extends Fruit> list;
    // Explicit type argument Fruit can be replaced with <> 意思就是new ArrayList<Fruit>() 可以写成 new ArrayList<>()
    list = new ArrayList<Fruit>();
    list = new ArrayList<Apple>();
    list = new ArrayList<Banana>();
    return list;
}

list的具体定义是new ArrayList(), new ArrayList(),还是new ArrayList(),在m1中是不知道的,最大的泛型是new ArrayList(),所以取出来的一定是Fruit,最小泛型new ArrayList(),new ArrayList()…可能有很多个,具体是哪个没办法确定,所以也没办法往里面添加元素

代码语言:javascript
复制
private static void m1(List<? extends Fruit> list) {
    /**
     * 这里获取到的 list 可能是ArrayList<Banana>,ArrayList<Apple>,ArrayList<Fruit>,
     * 具体是哪一种,这里不知道,所以获取元素时只能按照ArrayList<Fruit>()算,获取到的元素一定是Fruit
     * 没法确定具体的类型就没法添加元素, 例如使用list.add(new Apple())但是list可能是new ArrayList<Banana>()
     */
    Fruit fruit = list.get(0);
    // list.add(new Apple());
}

List<? super Fruit> list的理解

正如表面意思,泛型的类型是Fruit或者Fruit的父类,也就是我们给list赋值时泛型可以写Fruit或者Fruit的父类,可以是new ArrayList()也可以是 new ArrayList();

如果理解成"list中的元素是Fruit或Fruit的父类,仔细品品,漏洞百出,试想如果Fruit是个顶级接口呢?那岂不是list中只能放Object了?而且还只能是Object类本身?"

代码语言:javascript
复制
private static List<? super Fruit> getSuperList() {
    List<? super Fruit> list;
    list = new ArrayList<Fruit>();
    list = new ArrayList<Object>();
    return list;
}

list的具体定义到底是new ArrayList()或者new ArrayList()在m2方法中是没法确定的,所以往list中添加元素只能按照最小泛型处理,即按照new ArrayList()处理,获取元素时按照最大的泛型处理,即new ArrayList(),所以拿到的元素都是Object(都Object了,泛型也就没意义了),所以<? super Fruit>的意义在于往集合中添加元素,也就是"消费者只能用super"

代码语言:javascript
复制
private static void m2(List<? super Fruit> list) {
    /**
     * 这里接收到的list 可能是 ArrayList<Fruit>,ArrayList<Object>,
     * 具体是哪一种,这里不清楚,所以添加元素时只能按照 ArrayList<Fruit>算,获取元素时只能按照ArrayList<Object>算
     */
    list.add(new Apple());
    list.add(new Banana());
    // 获取到的是Object 泛型对于获取元素没啥意义
    Object object = list.get(0);
}

总结

  1. List<? extends Fruit> list 限定通配符泛型(还不确定的泛型,但是有范围),一般用于只获取
  2. List<? super Fruit> list 限定通配符泛型(还不确定的泛型,但是有范围), 一般用于只添加(也可以获取,但是获取出来的是Object,没啥意义)
  3. List list 明确的泛型,可获取,也可添加,也是我们用的最多的泛型

JDK中的PECS

java.util.Collections#copy

代码语言:javascript
复制
public static <T> void copy(List<? super T> dest, List<? extends T> src) {
    int srcSize = src.size();
    if (srcSize > dest.size())
        throw new IndexOutOfBoundsException("Source does not fit in dest");

    if (srcSize < COPY_THRESHOLD ||
        (src instanceof RandomAccess && dest instanceof RandomAccess)) {
        for (int i=0; i<srcSize; i++)
            dest.set(i, src.get(i));
    } else {
        ListIterator<? super T> di=dest.listIterator();
        ListIterator<? extends T> si=src.listIterator();
        for (int i=0; i<srcSize; i++) {
            di.next();
            di.set(si.next());
        }
    }
}
本文参与?腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-03-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客?前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与?腾讯云自媒体同步曝光计划? ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • [简单粗暴]一文彻底搞懂Java泛型中的PECS原则(在坑里躺了多年终于爬出来了)
  • 两种限定通配符
  • PECS原则
  • 上代码
  • List<? extends Fruit> list 的理解
  • List<? super Fruit> list的理解
  • 总结
  • JDK中的PECS
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
http://www.vxiaotou.com