Java笔记:泛型、限定通配符与非限定通配符

2023-11-13

1 泛型

在集合中存储对象并在使用前进行类型转换是多么的不方便。泛型防止了那种情况的发生。 它提供了编译期的类型安全,确保你只能把正确类型的对象放入集合中,避免了在运行时出现ClassCastException。

  • 不使用泛型
/**
 * 这样做的一个不好的是Box里面现在只能装入String类型的元素,今后如果我们需要装入Integer等其他类型的元素,
 * 还必须要另外重写一个Box,代码得不到复用,使用泛型可以很好的解决这个问题。
 */
public class Box {
    private String object;

    public void set(String object) {
        this.object = object;
    }

    public String get(){
        return object;
    }
}
  • 使用泛型
public class  GenericBox<T> {
    // T stands for "Type"
    private T t;
    public void set(T t) { this.t = t; }
    public T get() { return t; }
}

2 限定通配符与非限定通配符

2.1 限定通配符

限定通配符对类型进行了限制。有两种限定通配符:

  1. <? extends T>它通过确保类型必须是T及T的子类来设定类型的上界;
  2. <? super T>它通过确保类型必须是T及T的父类设定类型的下界;

泛型类型必须用限定的类型来进行初始化,否则会导致编译错误。

2.2 非限定通配符

表示了非限定通配符,因为可以用任意类型来替代。

public class BoundaryCharExample {
    //查找一个泛型数组中大于某个特定元素的个数
    public static <T> int countGreaterThan(T[] array,T elem){
        int count = 0;
        for (T e : array) {
            if (e > elem) { // compiler error
                ++count;
            }
        }
        return count;
    }
    /*
    * comliler error:但是这样很明显是错误的,
    * 因为除了short, int, double, long, float, byte, char等原始类型,
    * 其他的类并不一定能使用操作符" > "
    * 解决 --> 使用限定通配符/边界符
    * */
}

使用限定通配符改进:

public class BoundaryCharExample2 {
    public static <T extends Comparable<T>> int countGreaterThan(T[] array,T elem){
        //<T extends Comparable<T>>就是通配符,类型必须是 Comparable<T>及其子类
        int count = 0;
        for (T e : array) {
            if (e.compareTo(elem)>0) {
                ++count;
            }
        }
        return count;
    }
}

3 PECS(Producer Extends Consumer Super)原则

  • Producer Extends:如果你需要一个只读List,用它来produce T,那么使用? extends T;
  • Consumer Super:如果你需要一个只写List,用它来consume T,那么使用? super T;

3.1 Producer Extends

对于实现了<? extends T>的集合类只能将它视为 Producer 向外提供(get)元素, 而不能作为 Consumer 向外获取(add)元素。

public class GenericExample {
    public static void main(String[] args) {
        List<? extends Fruit> fruits = new ArrayList<Apple>();
        //? extends Fruit表示的是Fruit及其子类
        
        // Compile Error: can't add any type of object:
        //fruits.add(new Apple());
        //fruits.add(new Orange());
        //fruits.add(new Fruit());
        //fruits.add(new Object());
        //fruits.add(null); // Legal but uninteresting
    }
}

Compile Error: can’t add any type of object:

从编译器的角度去考虑,List<? extends Fruit> fruits自身可以有多种含义:

List<? extends Fruit> fruits = new ArrayList<Fruit>();

List<? extends Fruit> fruits = new ArrayList<Apple>();

List<? extends Fruit> fruits = new ArrayList<Orange>();

// 这里Apple和Orange都是Fruit子类
  1. 当我们尝试add一个Apple的时候,fruits可能指向new ArrayList< Orange >();
  2. 当我们尝试add一个Orange的时候,fruits可能指向new ArrayList< Apple >();
  3. 当我们尝试add一个Fruit的时候,这个Fruit可以是任何类型的Fruit,而fruits可能只想是某种特定类型的Fruit,因此编译器无法识别,报错。

应用示例:

public class GenericReading {
    private List<Apple> apples = Arrays.asList(new Apple());
    private List<Fruit> fruit = Arrays.asList(new Fruit());

    private class Reader<T>{ //Reader<T> 是自定义的泛型类
         /*T readExact(List<T> list){ 
             return list.get(0);
         }*/
        T readExact(List<? extends T> list){// 使用通配符来解决这个问题
            // ? extends T 表示 T 及 T 的子类
            return list.get(0); //TODO :get()方法
        }
    }

    @Test
    public void test(){
        Reader<Fruit> fruitReader=new Reader<Fruit>();
        //Fruit f=fruitReader.readExact(apples);
        // 使用 readExact(List<T> list)  
        // Errors: List<Fruit> cannot be applied to List<Apple>.
        
        Fruit f=fruitReader.readExact(apples);//正确
        System.out.println(f);
    }
}

3.2 Consumer Super

对于实现了<? super T>的集合类只能将它视为 Consumer 向外获取(add)元素, 而不能作为 Producer 向外提供(get)元素。

从编译器的角度考虑,对于List<? super Apple> list,它可以有下面几种含义:

List<? super Apple> list = new ArrayList<Apple>();
List<? super Apple> list = new ArrayList<Fruit>();
List<? super Apple> list = new ArrayList<Object>();

当我们尝试通过list来get一个Apple的时候,可能会get得到一个Fruit,这个Fruit可以是Orange等其他类型的Fruit,因此编译器无法识别,报错。

应用示例:

public class GenericWriting {
    private List<Apple> apples = new ArrayList<Apple>();
    private List<Orange> oranges = new ArrayList<Orange>();
    private List<Fruit> fruit = new ArrayList<Fruit>();

    <T> void writeExact(List<T> list, T item) {
        list.add(item); //TODO :这里是 add
    }

    // ? super T 
    // T 及 T 的父类
    <T> void writeWithWildcard(List<? super T> list, T item) {
        list.add(item);
    }

    void func1(){
        writeExact(apples,new Apple());
        writeExact(fruit,new Apple());
    }

    void func2(){
        writeWithWildcard(apples, new Apple());
        writeWithWildcard(fruit, new Apple());
    }

    @Test
    public void test(){
        func1();
        func2();
    }
}

JDK 8 Collections.copy() 源码:

public static <T> void copy(List<? super T> dest, List<? extends T> src) {
        //dest 就是 只写的 List
        //src 就是 只读的 List
        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());
            }
        }
    }

RandomAccess是一个空的、用来标记的接口;

用处是当要实现某些算法时,会判断当前类是否实现了RandomAccess接口,会选择不同的算法;

接口RandomAccess中内容是空的,只是作为标记用。比如List下的ArrayList和LinkedList。其中ArrayList实现了RandomAccess。而LinkedList没有。我们可以利用instanceof来判断哪一个是实现了RandomAccess。分辨出两个集合。其中ArrayList使用for循环遍历快,而LinkedList使用迭代器快。那么通过分辨,不同的集合使用不同的遍历方式;

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java笔记:泛型、限定通配符与非限定通配符 的相关文章

  • 热重载在docker中运行的java程序

    我开发了一个java程序 应该在docker中运行 然而 我在调试docker中运行的java程序时遇到了很多痛苦 我在网上搜索 一些教程提出了像 spring dev tools 这样的工具 因为我的java程序是基于spring boo
  • 如果测试用例失败,Selenium Web 驱动程序无法关闭 Firefox 实例

    我各位 我正在使用 junit 和 selenium web 驱动程序 2 28 问题是 如果我运行成功的测试用例 Web 驱动器能够关闭 Firefox 实例 但是当测试用例失败时 Selenium Web 驱动器无法关闭 Firefox
  • JavaMail Gmail 问题。 “准备启动 TLS”然后失败

    mailServerProperties System getProperties mailServerProperties put mail smtp port 587 mailServerProperties put mail smtp
  • 如何将 Java 赋值表达式转换为 Kotlin

    java中的一些东西就像 int a 1 b 2 c 1 if a b c System out print true 现在它应该转换为 kotlin 就像 var a Int 1 var b Int 2 var c Int 1 if a
  • 如何测试 JUnit 测试的 Comparator?

    我需要测试 Compare 方法 但我对如何测试感到困惑 我可以看看该怎么做吗 public class MemberComparator implements Comparator
  • JNI 不满意链接错误

    我想创建一个简单的 JNI 层 我使用Visual studio 2008创建了一个dll Win 32控制台应用程序项目类型 带有DLL作为选项 当我调用本机方法时 出现此异常 Exception occurred during even
  • java.io.IOException: %1 不是有效的 Win32 应用程序

    我正在尝试对 XML 文档进行数字签名 为此我有两个选择 有一个由爱沙尼亚认证中心为程序员创建的库 还有一个由银行制作的运行 Java 代码的脚本 如果使用官方 认证中心 库 那么一切都会像魅力一样进行一些调整 但是当涉及到银行脚本时 它会
  • 使用 ANTLR 为 java 源代码生成抽象语法树

    如何使用 ANTLR 从 java src 代码生成 AST 有什么帮助吗 好的 步骤如下 前往ANTLR站点 http www antlr org 并下载最新版本 下载Java g和JavaTreeParser g文件来自here htt
  • Convert.FromBase64String 方法的 Java 等效项

    Java 中是否有相当于Convert FromBase64String http msdn microsoft com en us library system convert frombase64string aspx which 将指
  • java中删除字符串中的特殊字符?

    如何删除字符串中除 之外的特殊字符 现在我用 replaceAll w s 它删除了所有特殊字符 但我想保留 谁能告诉我我该怎么办 Use replaceAll w s 我所做的是将下划线和连字符添加到正则表达式中 我添加了一个 连字符之前
  • 如何在 Java 中禁用 System.out 以提高速度

    我正在用 Java 编写一个模拟重力的程序 其中有一堆日志语句 到 System out 我的程序运行速度非常慢 我认为日志记录可能是部分原因 有什么方法可以禁用 System out 以便我的程序在打印时不会变慢 或者我是否必须手动检查并
  • 在具有相同属性名称的不同数据类型上使用 ModelMapper

    我有两节课说Animal AnimalDto我想用ModelMapper将 Entity 转换为 DTO 反之亦然 但是对于具有相似名称的一些属性 这些类应该具有不同的数据类型 我该如何实现这一目标 动物 java public class
  • 反思 Groovy 脚本中声明的函数

    有没有一种方法可以获取 Groovy 脚本中声明的函数的反射数据 该脚本已通过GroovyShell目的 具体来说 我想枚举脚本中的函数并访问附加到它们的注释 Put this到 Groovy 脚本的最后一行 它将作为脚本的返回值 a la
  • 如何在 JFreeChart TimeSeries 图表上显示降雨指数和温度?

    目前 我的 TimeSeries 图表每 2 秒显示一个位置的温度 现在 如果我想每2秒显示一次降雨指数和温度 我该如何实现呢 这是我的代码 import testWeatherService TestWeatherTimeLapseSer
  • 制作java包

    我的 Java 类组织变得有点混乱 所以我要回顾一下我在 Java 学习中跳过的东西 类路径 我无法安静地将心爱的类编译到我为它们创建的包中 这是我的文件夹层次结构 com david Greet java greeter SayHello
  • 查看Jasper报告执行的SQL

    运行 Jasper 报表 其中 SQL 嵌入到报表文件 jrxml 中 时 是否可以看到执行的 SQL 理想情况下 我还想查看替换每个 P 占位符的值 Cheers Don JasperReports 使用 Jakarta Commons
  • 将 JTextArea 内容写入文件

    我在 Java Swing 中有一个 JTextArea 和一个 提交 按钮 需要将textarea的内容写入一个带有换行符的文件中 我得到的输出是这样的 它被写为文件中的一个字符串 try BufferedWriter fileOut n
  • 休眠以持久保存日期

    有没有办法告诉 Hibernate java util Date 应该持久保存 我需要这个来解决 MySQL 中缺少的毫秒分辨率问题 您能想到这种方法有什么缺点吗 您可以自己创建字段long 或者使用自定义的UserType 实施后User
  • 中断连接套接字

    我有一个 GUI 其中包含要连接的服务器列表 如果用户单击服务器 则会连接到该服务器 如果用户单击第二个服务器 它将断开第一个服务器的连接并连接到第二个服务器 每个新连接都在一个新线程中运行 以便程序可以执行其他任务 但是 如果用户在第一个
  • JAVA - 如何从扫描仪读取文件中检测到“\n”字符

    第一次海报 我在读取文本文件的扫描仪中读取返回字符时遇到问题 正在读取的文本文件如下所示 test txt start 2 0 30 30 1 1 90 30 0 test txt end 第一行 2 表示两个点 第二行 位置索引 0 xp

随机推荐

  • redis数据类型(图解)

    一些命令的使用场景 1 由于集合是无序的 spop命令可以用于抽奖活动 2 商品销量排行榜可以用zset做 3 setnx命令实现分布式锁 4 消息队列模型 lpush rpop 以上是本人的学习笔记 若有不对的地方 请大家指正 转载于 h
  • 【多模态】3、CLIP

    文章目录 一 背景 二 方法 2 1 使用自然语言来监督训练 2 2 建立一个超大数据集 2 3 选择预训练的方式 对比学习而非预测学习 2 4 模型缩放和选择 三 效果 四 思考 论文 Learning Transferable Visu
  • python函数编程 返回函数 匿名函数 装饰器 偏函数

    返回函数 函数的返回对象可以是一个函数 返回时不立即执行 而是调用返回对象的时候再执行 def lazy sum args def sum ax 0 for n in args ax ax n return ax return sum gt
  • Python边学边用--BT客户端实现之BitTorrent文件解析

    BitTorrent文件使用bencode编码 其中包括了4种数据类型 d 开头表示是dict类型 e 表示结束 l 小写字母L 开头表示是list类型 e 表示结束 i 开头表示是integer类型 e 表示结束 可以表示负数 以数字开头
  • lisa traffic sign 数据集训练

    1 lisa下载地址 http cvrr ucsd edu LISA datasets html 2 解压缩后 使用python tools splitAnnotationFiles py将数据集划分成训练集和测试集的csv描述文件 3 转
  • 企业小程序订单管理系统解决痛点文档记录

    订单管理系统关于企业订货的痛点和介绍 一 适用行业 适用于批发 生产 代理 加盟 二 企业销售批发的痛点 在实际业务环境中 无论是厂家 生产 代理 批发 加盟 企业都会有自己的客户 这些客户可能是代理商 固定散客 加盟商等会遇到如下情况 在
  • 【Spark NLP】第 6 章:信息检索

    大家好 我是Sonhhxg 柒 希望你看完之后 能对你有所帮助 不足请指正 共同学习交流 个人主页 Sonhhxg 柒的博客 CSDN博客 欢迎各位 点赞 收藏 留言 系列专栏 机器学习 ML 自然语言处理 NLP 深度学习 DL fore
  • 忘记文档密码,教你破解WORD/EXECL/PPT文件加密密码

    大家办公时 有设置密码习惯 并且容易忘记密码 今天给大家提供一款超好用得小工具 不定时更新软件 高效率工具小福利 软件 Advanced OfficePassword Recovery 今天给大家带来一款破解Excel密码的神器 涉及到重要
  • VLC解码播放H264文件

    转自 http www cnblogs com ImageVision p 4744391 html utm source tuicool utm medium referral 昨天收到几个文件名是 xxx 264的文件 这种文件属于视频
  • web开发技术总结

    web开发可以理解为动态网站的开发 以java语言为例 就是基于java动态网站的开发 前台框架 jQuery Mvc框架 Struts spring Mvc 核心框架 Spring orm框架 Hibernate Spring JDBC
  • 复数乘法是什么?

    逛木虫的时候看到一个很旧的数学帖子被人挖了坟 这个帖子大概是讨论如果把复数看作是向量 那么复数乘法应该怎么看待 向量之间有乘法 例如复数 1 i 和复数 i 其对应的向量分别是 left begin array 20 c 1 1 end a
  • git没有冲突 但是提示有_git 处理冲突步骤

    背景 工程中有一块功能是在别的远程分支上的 然后自己的分支也是一直在更新的 现在要将该分支上的信功能合到自己的分支上 于是采用了git cherry pick的方法 但是出现了报错 查了许多网上的资料最后总结出处理冲突的步骤 具体实现 输入
  • [实习]Skywalking

    SkyWalking 1 是什么 skywalking是一个包含监控 追踪 并拥有故障诊断能力的分布式系统 它主要的作用是全链路监控 收集数据 分析处理数据 然后可视化呈现 这么说有点抽象 接下来画图来说 这是skywalking的架构 它
  • VscodeSSH免密远程登录服务器

    1 windows下cmd或git bash 或powershell等输入 ssh keygen 指令输入后一直回车 在C Users user name ssh路径下生成如下文件 2 linux服务器Terminal输入 ssh keyg
  • 【SVN命令】之 revert

    名称 子命令Svn revert 取消所有的本地编辑 概要 子命令Svn revert PATH 描述 Reverts any local changes to a file or directory and resolves any co
  • nodejs HelloWorld

    nodejs 服务器端 HelloWorld 程序 a hello js d02 hollo js var http require http http createServer function request response 请求对象
  • C++&Qt 各种数据类型转换

    1 uint64转QString QString strfilerename QString 1 arg nFileID nFileID为uint64类型 QString number nFileID 2 QString转超长数字串 QSt
  • 计算机图形学GAMES101(三)变换(模型、视图、投影)

    补充内容 R 是逆时针方向旋转的矩阵 R 是顺时针方向旋转的矩阵 可以发现R T R 1 像这样的矩阵叫做正交矩阵 以后如果要求往相反的方向旋转相同角度的变换 R 只需要求正向旋转的矩阵然后转置就可以了 本节涉及内容 仿射变换 线性变换 平
  • LeetCode-Python-389. 找不同

    给定两个字符串 s 和 t 它们只包含小写字母 字符串 t 由字符串 s 随机重排 然后在随机位置添加一个字母 请找出在 t 中被添加的字母 示例 输入 s abcd t abcde 输出 e 解释 e 是那个被添加的字母 第一种思路 转成
  • Java笔记:泛型、限定通配符与非限定通配符

    目录 1 泛型 2 限定通配符与非限定通配符 2 1 限定通配符 2 2 非限定通配符 3 PECS Producer Extends Consumer Super 原则 3 1 Producer Extends 3 2 Consumer