【再探】Java—泛型

news/2024/6/17 1:41:09/文章来源:https://blog.csdn.net/qq_25308331/article/details/139160858

 Java 泛型本质是参数化类型,可以用在类、接口和方法的创建中。

1 “擦除式”泛型

Java的“擦除式”的泛型实现一直受到开发者的诟病。

“擦除式”的实现几乎只需要在Javac编译器上做出改进即可,不要改动字节码、虚拟机,也保证了以前没有使用泛型的库可以之间运行在java 5.0 之上。但是这个带来了以下弊端:

  1. 例如对于List<String> 和 List<Integer> ,在运行时由于擦除了,所以这两个都变成了List,因此它们在运行中是同一类型。(而对于C#的泛型来说,无论在源码中、编译后及运行时它们始终是不同的类型)。这导致在运行时,获取不到类型信息。
  2. “擦除式”的实现,是在元素被赋值时编译器自动插入类型检查指令,访问元素时,自动插入类型强制转换指令。这样频繁的类型检查及转换,导致Java的泛型性能差于C#的泛型。
public class EraseGeneric {private static class Holder<T> {T t;public T getT() {return t;}public void setT(T t) {this.t = t;}}public static void main(String[] args) {Holder<String> holder = new Holder<>();holder.setT("hello");String str = holder.getT();}}

图 Holder类被编译后的字节码片段

图 main 方法中,对于Holder类型的赋值及访问操作字节码

1.1 擦除的补偿

如果要在运行时获取类型信息,那么可以通过引入类型标签来对擦除进行补偿。

public class TypeTagGeneric {private static class User {public User() {}}private static <T> void fun(Class<T> kind) throws InstantiationException, IllegalAccessException {T t = kind.newInstance();System.out.println(t);}public static void main(String[] args) throws InstantiationException, IllegalAccessException {fun(User.class);int[] array1 = new int[10];String[] array2 = new String[10];}}

2 协变与逆变

A 类型是B的父类型,对于某个构造器,构造出的复杂类型A`与B`。

协变

A`仍然是B`的父类型。比如Java中的数组,A[] 仍是B[]的父类型。

逆变

B`是A`的父类型。

抗变

A`与B`没有任何继承关系。例如List<A> 与List<B>没有任何继承关系。

表 协变、逆变与抗变

2.1 数组与泛型

T[] t = new T[10]; 这个代码是错误的,Java中规定不能创建泛型数组。

因为Java 在运行时,无法获取泛型的类型信息,因为在创建数组时,也就无法获取到泛型参数所表示的确切类型。

2.1.1 数组的类型

Java中数组的种类有两种:

  1. 基础类型的数组:[ + 开头大写字母。

int[] : [I

  1. 引用类型的数组:[ + L + 类型。

String[] array2 : [Ljava/lang/String

public class ArrayGeneric {private static class Fruit {}private static class Apple extends Fruit {}public static void main(String[] args) {Fruit[] fruits = new Fruit[10];Apple[] apples = new Apple[10];System.out.println(fruits instanceof Fruit[]); // trueSystem.out.println(fruits instanceof Apple[]); // falseSystem.out.println(apples instanceof Fruit[]); // trueSystem.out.println(apples instanceof Apple[]); // truefruits = apples;
//        apples = fruits; // 编译错误System.out.println(fruits.getClass().getSuperclass()); // class java.lang.ObjectSystem.out.println(apples.getClass().getSuperclass()); // class java.lang.Object
//        getSuperclass() 方法:如果此 Class 表示 Object 类、一个接口、一个基本类型或 void,则返回 null。
//        如果此对象表示一个数组类,则返回表示该 Object 类的 Class 对象。否则返回该类的超类。}}

2.2 通配符

泛型中的通配符用于在两个类型之间建立某种类型的向上转型关系。

协变

? extends T, 例如List<? extends Fruit> list。确定了元素类型的父类为Fruit,但不能确定其确切类型,因此不能往该容器添加新的元素(只能添加null)。但是可以从容器中提取元素,类型为Fruit。

逆变

? super T,例如List<? super Apple> list,确定了元素为Apple的父类,因为可以往容器中添加元素,但不能提取元素。

表 通配符的协变与逆变

public class CovarianceAndContravariance {private static class Fruit {}private static class Apple extends Fruit {}private static <T extends Apple> void setItem(List<? super Apple> list, T item) {list.add(item);}private static Fruit getItem(List<? extends Fruit> list,int pos) {return list.get(pos);}public static void main(String[] args) {List<? super Apple> list = new ArrayList<>();setItem(list,new Apple());List<? extends Fruit> list2 = Arrays.asList(new Fruit(),new Apple());Fruit item = getItem(list2, 0);}}

2.2.1 无界通配符

无界通配符,例如List<?>, 其相当于List<? extends Object>,但不等价于List(相当于List<Object>)。其有两个作用:

  1. 告诉编译器,我用了泛型,只是还没确定哪个类型;
  2. 用于捕获类型。
public class CaptureGeneric {private static class Holder<T> {}private static <T> void fun1(Holder<T> holder) {System.out.println(holder);}private static void fun2(Holder<?> holder) {fun1(holder);}public static void main(String[] args) {Holder holder = new Holder(); //  原生类型fun1(holder); // 警告,Unchecked assignment:fun2(holder); // 不会警告,无边界通配符将不会这个原生类型的类型参数(Object)}}

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

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【MiniCPM-V】win10本地部署OCR等性能测试

性能尝试 本地配置如下 --------------------------------------------------------------------------------------- | NVIDIA-SMI 546.80 Driver Version: 546.80 CUDA Version: 12.3 | |-----------------------------------------------------…

ClickHouse

1 .ClickHouse 的特点 ClickHouse 是俄罗斯的 Yandex 于 2016 年开源的列式存储数据库&#xff08;DBMS&#xff09;&#xff0c;使用 C 语言编写&#xff0c;主要用于在线分析处理查询&#xff08;OLAP&#xff09;&#xff0c;能够使用 SQL 查询实时生成分析数据报告。 OLA…

免费发布web APP的四个途径(Python和R)

免费发布数据分析类&#x1f310;web APP的几个途径&#x1f4f1; 数据分析类web APP目前用来部署生信工具&#xff0c;统计工具和预测模型等&#xff0c;便利快捷&#xff0c;深受大家喜爱。而一个免费的APP部署途径&#xff0c;对于开发和测试APP都是必要的。根据笔者的经验…

深度融合大语言模型与知识图谱:思通数科企业知识库智能问答系统的创新实践

摘要 在知识经济时代&#xff0c;企业知识管理的重要性日益凸显。本文深入探讨了思通数科如何利用大语言模型和知识图谱技术&#xff0c;构建企业知识库智能问答系统&#xff0c;以促进知识的高效获取、共享、应用和创新&#xff0c;从而提升企业的知识管理水平和业务价值。 1…

2024定制版抢单支付系统源码(开代理自动抢单接单)

随着网络和移动支付技术的不断进步&#xff0c;抢单支付系统已经成为商家和用户进行交易的便利工具。2024定制版抢单支付系统源码为开发者提供了一个可定制化的解决方案&#xff0c;具备开放代理和自动抢单接单功能&#xff0c;帮助用户快速搭建抢单支付平台。本文将为您介绍这…

express.js--连接数据库,并且增删改查(四)

使用数据库需要在电脑安装mysql&#xff0c;然后使用navicat 我没有下载mysql,我使用的是小皮里面的数据库&#xff0c;需要破解版的navicat可以私信我 安装mysql npm i mysql 数据库的基本信息&#xff0c;我是直接写到配置文件里面的 config/index.js module.exports {…

作业-day-240523

思维导图 知识点问答 1、IO多路复用的原理 1、创建一个检测文件描述符的容器 fd_set fds; 2、将需要检测的文件描述符放入容器中 FD_SET(文件描述符&#xff0c;&fds); 3、通过一个阻塞函数阻塞等待容器中是否有事件产生&#xff0c;如果有一个或多个事件产生&#xff0c…

苹果CMS:采集参数设置

我们安装苹果CMS参考苹果cms&#xff1a;介绍及安装&#xff0c;安装好设置采集器苹果CMS&#xff1a;怎么采集&#xff0c;配置采集深度&#xff08;即爬取链接的层次&#xff09;&#xff0c;以及是否遵循robots.txt协议。采集插件通常需要用户自定义匹配规则来解析目标网页内…

C++三剑客之std::any(二) : 源码剖析

目录 1.引言 2.std::any的存储分析 3._Any_big_RTTI与_Any_small_RTTI 4.std::any的构造函数 4.1.从std::any构造 4.2.可变参数模板构造函数 4.3.赋值构造与emplace函数 5.reset函数 6._Cast函数 7.make_any模版函数 8.std::any_cast函数 9.总结 1.引言 C三剑客之s…

文盘Rust -- Mutex解决并发写文件乱序问题

在实际开发过程中&#xff0c;我们可能会遇到并发写文件的场景&#xff0c;如果处理不当很可能出现文件内容乱序问题。下面我们通过一个示例程序描述这一过程并给出解决该问题的方法。 use std::{fs::{self, File, OpenOptions},io::{Write},sync::Arc,time::{SystemTime, UNI…

262 基于matlab的一级倒立摆仿真

基于matlab的一级倒立摆仿真&#xff0c;在对一级倒立摆进行数学建模的基础上&#xff0c;对模型进行线性化&#xff0c;得到其状态空间模型&#xff0c;利用二次型最优控制方法得出控制率。输出角度和位置优化曲线。程序已调通&#xff0c;可直接运行。 262 一级倒立摆仿真 状…

人类听觉处理和语言中枢

人类听觉概述 人类听觉是指通过耳朵接收声音并将其转化为神经信号&#xff0c;从而使我们能够感知和理解声音信息的能力。听觉是人类五种感觉之一&#xff0c;对我们的日常生活和交流至关重要。 听觉是人类交流和沟通的重要工具。通过听觉&#xff0c;我们能够听到他人的语言…

冬奥会|基于SprinBoot+vue的冬奥会科普平台(源码+数据库+文档)

目录 基于SprinBootvue的冬奥会科普平台 一、前言 二、系统设计 三、系统功能设计 1登录注册 2系统功能模块 3管理员功能模块 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&#xff1a;✌️大厂码农|…

跨域计算芯片,一把被忽视的汽车降本尖刀

作者 |王博 编辑 |德新 2019年前后&#xff0c;「中央运算单元区域控制」的架构被提出。基于这一趋势&#xff0c;从板级的多芯片&#xff0c;到板级的单芯片&#xff0c;集成度越来越高&#xff0c;跨域计算芯片随之来到聚光灯下。 跨域计算芯片的特点是&#xff0c;与专为智…

音质全新升级,通信进化:从8K到16K采样率的全双工音频技术

思为无线惊喜升级SA618系列模块&#xff0c; 包括SA618F22,SA618F30&#xff0c; SA628F22, SA628F30&#xff0c; 全双工音频模块采样率由原来的8K升级为现在的16K&#xff0c;采用远距离无线传输&#xff0c;为了迎合不同客户的应用需求&#xff0c;我们将此系列音频音质升级…

JDK8:用java.nio.file.Files.lines方法读取大型文件

先说结论&#xff1a; 如果要读取一个大文件&#xff08;文件大小超过了内存大小&#xff09;&#xff0c;则可以考虑使用java.nio.file.Files.lines方法来读取这个大型文件的内容。 关于java.nio.file.Files类中lines方法的说明&#xff1a; jdk1.8.0_311中原码部分&#xf…

5.23 学习总结

一.项目优化&#xff08;语音通话&#xff09; 实现步骤&#xff1a; 1.用户发送通话申请&#xff0c;并处理通话请求&#xff0c;如果同意&#xff0c;为两个用户之间进行连接。 2.获取到电脑的麦克风和扬声器&#xff0c;将获取到的语音信息转换成以字节数组的形式传递。 …

基于 LlaMA 3 + LangGraph 在windows本地部署大模型 (十一)

基于 LlaMA 3 LangGraph 在windows本地部署大模型 &#xff08;十一&#xff09; LlaMA 3 系列博客 基于 LlaMA 3 LangGraph 在windows本地部署大模型 &#xff08;一&#xff09; 基于 LlaMA 3 LangGraph 在windows本地部署大模型 &#xff08;二&#xff09; 基于 Lla…

Excel中Lookup函数

#Excel查找函数最常用的是Vlookup&#xff0c;而且是经常用其精确查找。Lookup函数的强大之处在于其“二分法”的原理。 LOOKUP&#xff08;查找值&#xff0c;查找区域&#xff08;Vector/Array&#xff09;&#xff0c;[返回结果区域]&#xff09; 为什么查找区域必须升序/…

Mac启用三指拖移

1. 简介 在使用mac的用户中大部分都会开启三指拖移功能&#xff0c;它能大大简化选中文字、修改窗口大小、拖动窗口等操作。 什么是三指拖移&#xff1f; 三指拖移是macOS触控板的一种手势功能&#xff0c;允许用户用三根手指在触控板上进行拖动操作&#xff0c;而无需按住触…