java泛型6

news2024/12/25 9:34:30

到底何时使用泛型方法?何时使用类型通配符呢?大多数时候都可以使用泛型方法来代替类型通配符。
在这里插入图片描述
在这里插入图片描述
这种场景下效果一样。
上面方法使用了泛型形式,这时定义泛型形参时设定上限(其中E是Collection接口里定义的泛型,在该接口里E可当成普通类型使用)。
1.什么时候用通配符?
上面两个方法中泛型形参T只使用了一次,泛型形参T产生的唯一效果是可以在不同的调用点传入不同的实际类型。对于这种情况,应该使用通配符:通配符就是被设计用来支持灵活的子类化的。泛型方法允许泛型形参被用来表示方法的一个或多个参数之间的类型依赖关系,或者方法返回值与参数之间的类型依赖关系。如果没有这样的类型依赖关系,就不应该使用泛型方法。
2.什么时候用泛型方法?
如果某个方法中一个形参(a)的类型或返回值的类型依赖于另一个形参(b)的类型,则形参(b)的类型声明不应该使用通配符—因为形参(a)或返回值的类型依赖于该形参(b)的类型,如果形参(b)的类型无法确定,程序就无法定义形参(a)的类型。在这种情况下,只能考虑使用在方法签名中声明泛型,也就是泛型方法。
3.如果有需要,可以同时使用泛型方法和通配符,如Java的Collections.copy()方法。
在这里插入图片描述
上面copy方法中的dest和src存在明显的依赖关系,从源List中复制出来的元素,必须可以“丢进”目标List中,所以源List集合元素的类型只能是目标集合元素的类型的子类型或者它本身。但JDK定义src形参类型时使用的是类型通配符,而不是泛型方法。这是因为该方法无须向src集合中添加元素,也无须修改src集合里的元素,所以可以使用类型通配符,无须使用泛型方法。
简而言之,指定上限的类型通配符支持协变,因此这种协变的集合可以安全地取出元素(协变只出不进),因此无须使用泛型方法。当然,也可以将上面的方法签名改为使用泛型方法,不使用类型通配符,如下所示。
在这里插入图片描述
这个方法签名可以代替前面的方法签名。但注意上面的泛型形参 S,它仅使用了一次,其他参数的类型、方法返回值的类型都不依赖于它,那泛型形参S就没有存在的必要,即可以用通配符来代替S。使用通配符比使用泛型方法(在方法签名中显式声明泛型形参)更加清晰和准确,因此Java设计该方法时采用了通配符,而不是泛型方法。
类型通配符与泛型方法(在方法签名中显式声明泛型形参)还有一个显著的区别:类型通配符既可以在方法签名中定义形参的类型,也可以用于定义变量的类型;但泛型方法中的泛型形参必须在对应方法中显式声明。

补充几个知识点
1.泛型构造器—>菱形语法
正如泛型方法允许在方法签名中声明泛型形参一样,Java也允许在构造器签名中声明泛型形参,这样就产生了所谓的泛型构造器。一旦定义了泛型构造器,接下来在调用构造器时,就不仅可以让Java根据数据参数的类型来“推断”泛型形参的类型,而且程序员也可以显式地为构造器中的泛型形参指定实际的类型。
在这里插入图片描述
程序中①号代码不仅显式指定了泛型构造器中的泛型形参T的类型应该是String,而且程序传给该构造器的参数值也是String类型,因此程序完全正常。
但在②号代码处,程序显式指定了泛型构造器中的泛型形参T的类型应该是String,但实际传给该构造器的参数值是Double类型,因此这行代码将会出现错误。
Java 7新增的“菱形”语法,它允许调用构造器时在构造器后使用一对尖括号来代表泛型信息。但如果程序显式指定了泛型构造器中声明的泛型形参的实际类型,则不可以使用“菱形”语法。
在这里插入图片描述
类的生命中用了泛型形参E,构造器用了泛型T,玩的溜!!!

2.泛型方法与方法重载
因为泛型既允许设定通配符的上限,也允许设定通配符的下限,从而允许在一个类里包含如下两个方法定义。
在这里插入图片描述
上面的MyUtils类中包含两个copy()方法,这两个方法的参数列表存在一定的区别,但这种区别不是很明确:这两个方法的两个参数都是Collection对象,前一个集合里的集合元素类型是后一个集合里集合元素类型的父类。如果只是在该类中定义这两个方法不会有任何错误,但只要调用这个方法就会引起编译错误。例如,对于如下代码:
在这里插入图片描述
上面程序中粗体字部分调用copy()方法,但这个copy()方法既可以匹配①号copy()方法,此时泛型T表示的类型是Number;也可以匹配 ②号copy()方法,此时泛型T表示的类型是Integer。编译器无法确定这行代码想调用哪个copy()方法,所以这行代码将引起编译错误。

3.类型推断
Java 8改进了泛型方法的类型推断能力,类型推断主要有如下两方面。
1.可通过调用方法的上下文来推断泛型的目标类型。
2.可在方法调用链中,将推断得到的泛型传递到最后一个方法。
如下程序示范了Java 8对泛型方法的类型推断。
在这里插入图片描述
上面程序中前两行粗体字代码的作用完全相同,但第1行粗体字代码无须在调用MyUtil类的nil()方法时显式指定泛型参数为String,这是因为程序需要将该方法的返回值赋值给MyUtil类型,因此系统可以自动推断出此处的泛型参数为String类型。
上面程序中第3行与第4行粗体字代码的作用也完全相同,但第3行粗体字代码也无须在调用MyUtil类的nil()方法时显式指定泛型参数为Integer,这是因为程序将nil()方法的返回值作为了MyUtil类的cons()方法的第二个参数,而程序可以根据cons()方法的第一个参数(42)推断出此处的泛型参数为Integer类型。需要指出的是,虽然Java 8增强了泛型推断的能力,但泛型推断不是万能的,例如如下代码就是错误的。
在这里插入图片描述
因此,上面这行代码必须显式指定泛型的实际类型,即将代码改为如下形式:在这里插入图片描述

4.擦除和转换
在严格的泛型代码里,带泛型声明的类总应该带着类型参数。但为了与老的Java代码保持一致,也允许在使用带泛型声明的类时不指定实际的类型。如果没有为这个泛型类指定实际的类型,此时被称作raw type(原始类型),默认是声明该泛型形参时指定的第一个上限
类型。
当把一个具有泛型信息的对象赋给另一个没有泛型信息的变量时 ,所有在尖括号之间的类型信息都将被扔掉,比如一个List类型被转换为List,则该List对集合元素的类型检查变成了泛型参数的上限(即Object)。
了一个List对象,这个List对象保留了集合元素的类型信息。当把这个List对象赋给一 个List类型的list后,编译器就会丢失前者的泛型信息,即丢失list集合里元素的类型信息,这是典型的擦除。
后续继续研究,感觉用处不是很大!

5.泛型与数组
Java泛型有一个很重要的设计原则—如果一段代码在编译时没有 提出“[unchecked] 未经检查的转换”警告,则程序在运行时不会引发ClassCastException异常。正是基于这个原因,所以数组元素的类型不能包含泛型变量或泛型形参,除非是无上限的类型通配符。但可以声明元素类型包含泛型变量或泛型形参的数组。也就是说,只能声明List[]形式的数组,但不能创建ArrayList[10]这样的数组对象。
假设Java支持创建ArrayList[10]这样的数组对象,则有如下程序:
在这里插入图片描述
在上面代码中,如果粗体字代码是合法的,经过中间系列的程序运行,势必在①处引发运行时异常,这就违背了Java泛型的设计原则。
如果将程序改为如下形式:
在这里插入图片描述
上面程序粗体字代码行声明了List[]类型的数组变量,这是允许的;但不允许创建List[]类型的对象,所以创建了一个类型为ArrayList[10]的数组对象,这也是允许的。只是把ArrayList[10]对象赋值给List[]变量时会有编译警告“[unchecked] 未经检查的转换”,即编译器并不保证这段代码是类型安全的。上面代码同样会在①处引发运行时异常,但因为编译器已经提出了警告,所以完全可能出现这种异常。
Java允许创建无上限的通配符泛型数组,例如new ArrayList<?>[10],因此也可以将第一段代码改为使用无上限的通配符泛型数组, 在这种情况下,程序不得不进行强制类型转换。正如前面所介绍的, 在进行强制类型转换之前应通过instanceof运算符来保证它的数据类
型。将上面代码改为如下形式(程序清单同上):
在这里插入图片描述
与此类似的是,创建元素类型是泛型类型的数组对象也将导致编译错误。如下代码所示。
在这里插入图片描述
由于类型变量在运行时并不存在,而编译器无法确定实际类型是什么,因此编译器在粗体字代码处报错。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/189188.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

搭建企业知识库的意义

当客户跟你达成合作关系后&#xff0c;需要持续的关系维护&#xff0c;在一定的销售点&#xff0c;定期和客户沟通&#xff0c;据调查&#xff0c;赢得一个新客户的成本可能是保留一个现有客户的5到25倍&#xff0c;作为营销策略&#xff0c;客户服务支持必须满足他们的期望。建…

Java---微服务---Seata的部署和集成

Seata的部署和集成一、部署Seata的tc-server1.下载2.解压3.修改配置4.在nacos添加配置5.创建数据库表6.启动TC服务二、微服务集成seata1.引入依赖2.修改配置文件三、TC服务的高可用和异地容灾1.模拟异地容灾的TC集群2.将事务组映射配置到nacos3.微服务读取nacos配置一、部署Sea…

PEM格式RSA密钥解析(二)

PEM格式RSA密钥解析&#xff08;二&#xff09; RSA密钥参数解析 上一部分讲解了将Base64编码的密钥数据转换成hex格式数据&#xff0c;本章将介绍如何获从转码后的数据中获取RSA密钥的相关参数。 根据 RSA 密钥语法中的结构对私钥解析结果如下&#xff1a; 上一节转码后的私…

暗月ACK靶场 WP

环境搭建 https://mp.weixin.qq.com/s/VB4elHdrHNCmPDP_ktcLRg https://www.bilibili.com/video/BV1264y187St?spm_id_from333.1007.top_right_bar_window_history.content.click 按照文章拓扑根据实际情况搭建好&#xff0c;web2的其中一个网卡需要自己调一下ip 1、把 12s…

第五届字节跳动青训营 前端进阶学习笔记(十)Webpack基础

文章目录前言什么是Webpack1.Webpack概述2.Webpack基本使用Webpack打包核心流程1.webpack需要做的事情2.Webpack的使用3.处理CSS4.接入babel5.生成HTML6.HMR7.Tree-ShakingLoader1.Loader的主要特性2.常见Loader总结前言 课程重点&#xff1a; 什么是WebpackWebpack打包核心流…

本地启动打包后文件

本地启动打包后文件在 vs code 扩展中安装 Live Server配置 Live Server在 vs code 扩展中安装 Live Server 点击安装 Live Server 配置 Live Server VS Code setting.json 中配置 Vue/React 打包后文件夹 build/dist 为服务器的根目录 "liveServer.settings.root"…

AOP实例 – 环绕增强 日志记录

AOP实例 – 环绕增强 日志记录需求&#xff1a;记录图书的service层的日志操作&#xff0c;到数据库1. 创建商品日志记录表 Book_Log2. 完成日志记录表的插入逻辑3. 环绕增强搜集日志记录参数&#xff0c;并测试4. 环绕增强调用日志记录表的插入逻辑&#xff0c;完成日志记录功…

【目标检测论文解读复现NO.29】基于YOLO-ST的安全帽佩戴精确检测算法

前言此前出了目标改进算法专栏&#xff0c;但是对于应用于什么场景&#xff0c;需要什么改进方法对应与自己的应用场景有效果&#xff0c;并且多少改进点能发什么水平的文章&#xff0c;为解决大家的困惑&#xff0c;此系列文章旨在给大家解读最新目标检测算法论文&#xff0c;…

Spire.Barcode 5.1.0 for Java Patch

Spire.Barcode for Java是专门为开发人员设计的专业条码组件&#xff0c;用于在 Java 应用程序&#xff08;J2SE 和 J2EE&#xff09;上生成、读取和扫描 一维和二维条码。开发人员和程序员可以使用 Spire.Barcode 快速轻松地将企业级条码格式添加到他们的 Java 应用程序中。需…

【数字逻辑】逻辑函数式化简为其他形式

以 F&#xff08;A,B,C&#xff09;ABA‾CF&#xff08;A,B,C&#xff09; AB\overline{\rm A}CF&#xff08;A,B,C&#xff09;ABAC 为例&#xff0c;说明如何将与或式转换为其它类型的表达式。

CE认证和CCC认证区别?

CE认证和CCC认证区别? 参考链接:https://baijiahao.baidu.com/s?id=1728784934635704528&wfr=spider&for=pc CE认证和CCC认证有什么区别? 我发现我的很多朋友对此并不了解。 同时我发现很多产品在很多产品包装上都通过了CCC和CE认证,所以可能会引起没有做过产品…

硬件 -CPU工作原理

1.地址空间一个处理器能够访问&#xff08;读写&#xff09;的存储空间有限&#xff0c;我们称空间为地址空间&#xff0c;一般来说N位地址总线的处理器的地址空间是2的N次方。CPU从内存中取数据&#xff0c;先发地址&#xff0c;内存收到后&#xff0c;发送地址所在的数据。2.…

蓝奥声无线同步数据传输技术在物联网方面应用的优势

随着物联网技术的发展&#xff0c;其应用场景已经从简单的商品和服务扩展到广泛的社会和行业。当前&#xff0c;世界各地都在努力通过技术推动经济增长。对于许多行业来说&#xff0c;物联网技术的应用无疑将是一个新的增长机会。同时&#xff0c;物联网将推动社会结构从“制造…

Red Hat Linux 命令Crontab的使用方法

Red Hat Linux 命令Crontab的使用方法1 cron是一个linux下的定时执行工具&#xff0c;可以在无需人工干预的情况下运行作业。由于Cron 是Linux的内置服务&#xff0c;但它不自动起来&#xff0c;可以用以下的方法启动、关闭这个服务: /sbin/service crond start //启动服务 /…

C++的三大特性之继承

目录 一 继承的概念 代码&#xff1a; 总结&#xff1a; 二 继承中的关系 三 继承中的作用域问题 什么是域&#xff1f; 隐藏&#xff1a; 隐藏的场景&#xff1a; 总结 四 赋值兼容原则 什么是赋值兼容原则&#xff1f; 与平时强制类型转换的区别 这一个赋值兼容原则…

编译原理学习笔记18——语义分析和中间代码生成3

编译原理学习笔记18——语义分析和中间代码生成318.1 布尔表达式及其计算18.2 按数值表示法翻译布尔表达式18.3 带优化翻译布尔表达式18.1 布尔表达式及其计算 布尔表达式及其用途 计算布尔表达式的两种方法 计算布尔表达式的两种方法 18.2 按数值表示法翻译布尔表达式 数…

toString()、String.valueOf、(String)强转,有啥区别?

TOC 首先我们看这三头货&#xff0c;什么toString()、String.valueOf、(String)强转&#xff1f;有啥区别&#xff1f;就问你是不是很像&#xff0c;是不是都有一个String&#xff1f; 说白了&#xff0c;这三个都是将XX给变成String&#xff0c;简称变身String的方法 接下来一…

IDEA maven使用详解

前提 确保本地安装了maven 以一个模块工程为例&#xff0c;内部包含了许多maven项目 生命周期 这是全部的生命周期 其中&#xff0c;常用的生命周期的各个功能为&#xff1a; (clean生命周期)mvn clean清理target (default生命周期①)mvn validate验证项目为maven项目&a…

自然语言处理的进阶之路

1、隐马尔可夫链路一般较短原因 连乘链路太长&#xff0c;会导致数据稀疏&#xff0c;零频词太多 2、零频问题的一般解决方案 平滑/回退/差值 当n设置较小时&#xff0c;仍然会存在oov问题&#xff08;语料中未出现的词&#xff09; 2.1、平滑 2.1.1、加1平滑/拉普拉斯平…

Maven初级(一)

目录 一. Maven概述 1.1 Maven是什么 1.2 Maven的作用 1.2.1 项目构建 1.2.2 依赖管理 1.2.3 统一项目结构 1.3 Maven模型 1.3.1 插件 1.3.2 项目对象模型 1.3.3 依赖管理模型 二. Maven基础概念 2.1 仓库&#xff1a; 2.2 仓库分类 2.2.2 远程仓库(私服) 中央仓…