闲碎记事本 闲碎记事本
首页
  • JAVA
  • Cloudflare
  • 学完再改一遍UI
友链
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

YAN

我要偷偷记录...
首页
  • JAVA
  • Cloudflare
  • 学完再改一遍UI
友链
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • java

    • SpringBoot

    • SpringSecurity

    • MybatisPlus

    • Netty

    • sip

    • 其他

      • MDC 使用
      • 位运算
      • RedisMQ实现
      • 自定义枚举序列化
      • Mybatis使用自定义枚举
      • Jackson反序列化泛型注意点
      • 敏感词过滤算法
      • 线程
      • 并发学习
      • jni使用
      • 关于注释
      • 为什么一个Byte用两个16进制表示
      • JAVA获取系统信息
      • 对extends和super的理解
        • JAVA系统API
        • java探针初探
        • JAVA获取USB信息
        • HashMap初探
        • JAVA远程调试
        • 初探webflux
        • SSE示例
    • linux

    • docker

    • redis

    • nginx

    • mysql

    • 其他

    • 环境搭建

    • 知识库
    • java
    • 其他
    YAN
    2023-08-23
    目录

    对extends和super的理解

    在java中用extends和super来限定泛型的边界


    extends用来限制泛型上界

    语法<? extends T>,表示泛型类型只能为指定类型(T)或指定类型(T)的子类型,因此泛型范围<=T

    这意味着你只能读取的值(保底返回一个最大的T),但不能往容器里面写入(因为任意写入值的泛型范围有可能会超出T)。


    super用来限制泛型下界

    语法<? super T>,表示泛型类型必须为指定类型(T)或指定类型(T)的父类型, 因此泛型范围>=T

    这意味着你只能往这个容器里写入T或父类型为T的数据(也就是T的子类型)这将永远不会超出上限,但不能读取具体类型(因为无法得知具体的类型是T还是T的子类型)。


    # 通俗的理解

    假设有如下代码: Bread 和 Noodles 都继承于 Food

    public static class  Food{}
    
    public static class  Bread extends Food {}
    
    public static class  Noodles extends Food {}
    

    # 关于extends

    例如定义了一个 List<? extends Food> 最上级形态为 Food,

    我们给这个列表放置了Bread

    List<? extends Food> foods= new ArrayList<Bread>
    

    这时候我想要:foods.add(noodles) ,noodles 继承于 Bread,理论上来说这样是没问题的

    但是,此时的foods 类型为Bread 。把 noodles 放入 Bread 的列表显然不合理。

    但是此时无论获取到的是Bread 还是 noodles 他们都能称之为Bread

    所以当进行了上界修饰后的容器:失去了写入能力,保留读取能力

    # 关于super

    例如定义了一个 List<? super Food> 最下级形态为 Food,

    我们同样给这个列表放置Bread

    List<? super Food> foods= new ArrayList<Bread>();
    

    此时程序无法编译通过,因为 Bread 为Food 的子类型,这段代码超出了泛型的下界。

    根据下界的规则,可以进行如下修改

    方案1

    // 指定下界 Food ,范围为 Food 或 其父类型, 
    List<? super Food> foods= new ArrayList<Food>();
    
    //能写入`Food`,`Bread`,`Noodles` 类型
    

    方案2

    // 指定下界 Bread ,范围为 Bread 或 其父类型
    List<? super Bread> foods= new ArrayList<Food>();
    
    //只能,能写入`Bread` 类型
    

    以上两种修改都是正确的,区别在于泛型范围不一样,导致能写入的值不同

    方案1: foods本质为Food列表,无论是加入Bread,noodles显然都是合理的,但是在取出的时候,无法获取到具体的类型,仅仅只是知道都属于Food,失去了获取详细内容的能力

    方案2: 虽然 foods本质同样为Food列表,但由于泛型下界为Bread,foods列表只能写入Bread和其子类型,同样无法获取到准确类型。

    一些示例

    # 泛型类型上下限示例

        public static class  A{}
    
        public static class  B extends A {}
    
        @Test
        public void  test(){
            //泛型类型的上限是 A或 A 的子类型
            
            //编译通过 -> 赋值对象泛型A,在泛型访问内
            List<? extends  A> extend1 = new ArrayList<A>();
            
            //编译通过 -> 赋值对象泛型B, B是A的子类,在泛型访问内
            List<? extends  A> extend2 = new ArrayList<B>();
            
            //编译失败 -> 赋值对象泛型Object, Object不是A的子类,不在泛型访问内
            List<? extends  A> extend3 = new ArrayList<Object>();
            
            //编译通过 -> 赋值对象泛型为空,, 默认采用<? extends  A>
            List<? extends  A> extend3 = new ArrayList<>();
            
            
            //泛型类型的下限是 A或A的父类型
            
            //编译通过 ->  赋值对象泛型A,在泛型访问内
            List<? super   A> super1 = new ArrayList<A>();
            
            //编译失败 ->  赋值对象泛型B, B不是A的父类型,不在泛型访问内
            List<? super   A> super2 = new ArrayList<B>();
            
            //编译失败 ->  赋值对象泛型Object, Object不是A的父类型,不在泛型访问内
            List<? super   A> super3 = new ArrayList<Object>();
            
            //编译通过 ->  赋值对象泛型为空,, 默认采用<? super  A>
            List<? super   A> super3 = new ArrayList<>();
        }
    

    # 可写入读取的类型示例

    # extends示例

    extend 更关注结果

        public void  test(){
            //泛型范围为:A和A的子类型 ,所以A和B类型都是在泛型范围内,所以编译通过
            List&lt;? extends  A> extend = Arrays.asList(new A(),new B());
            
            //由于泛型范围为:A和A的子类型,所以在add的时候,无法确定?是A,B,又或者是其他的C
            //因此写入是不安全的
            extend.add(new A()); // 编译失败
            extend.add(new B()); // 编译失败
            
            //由于泛型上限为A,因此可以安全的获取到结果,编译通过
            extend.forEach(i-> System.out.println(i.getClass()));
         
    
        }
    

    # super示例

    super 更关注参数

        public void  test(){
            List<? super   B> super1 = new ArrayList<>();
            //泛型范围为:B和B的父类型,所以List的值可为:B类型和父类型为B的数据 (B和B的子类型)
            
            //编译失败 ->  A是不B的子类型
            super1.add(new A());
            //编译通过 ->  类型为范围下限B
            super1.add(new B());
            
            //泛型类型下限为B,内容:B或B的子类型,
            //因此这里无法获取到具体的数据类型,所以无法获取到准确的对象,只能返回Object
            Object o = super1.get(0);
        }
        
         public void  test2(){        
            List<? super   A> super1 = new ArrayList<>();
            
            //编译通过 ->  类型为范围下限A
            super1.add(new A());
            //编译通过 ->  B是A的子类型
            super1.add(new B());
            
            //泛型类型下限为A,内容:A或A的子类型,
            //因此这里无法获取到具体的数据类型,所以无法获取到准确的对象,只能返回Object
            Object o = super1.get(0);
        }
    
    上次更新: 2025/05/22, 07:52:48
    JAVA获取系统信息
    JAVA系统API

    ← JAVA获取系统信息 JAVA系统API→

    最近更新
    01
    Caddy操作指南
    04-25
    02
    虚拟机磁盘扩展
    04-22
    03
    Swap空间
    04-22
    更多文章>
    Theme by Vdoing | Copyright © 2022-2025 YAN | MIT License
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式