当前位置: 首页 > news >正文

【JavaSE】泛型

系列文章目录


提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 系列文章目录
  • 前言
  • 一、pandas是什么?
  • 二、使用步骤
    • 1.引入库
    • 2.读入数据
  • 总结


前言

        学习泛型之前请大家先详细地了解一下,关于Java当中的包装类和Object类的概念以及包装类当中的自动装箱和自动拆箱是什么,如果这部分内容不理解的话,泛型当中的很多细节可能无法理解,这篇文章集中讲解泛型当中的重难点,对于简单的细节不做介绍。


一、什么是泛型

1、基础概念

        泛型是一种在编程语言中实现参数化类型的机制,它允许在定义类、接口、方法时使用类型参数,以便在使用时指定具体的类型。Java中引入了泛型的概念,它提供了编译时的类型检查,并允许在编写通用代码时指定具体的数据类型。

2、主要特点

        1)参数化类型:泛型允许类、接口、方法在定义时使用参数,这些参数可以在使用时被具体的类型替代。

        2)类型安全:泛型提供了编译时的类型检查机制,避免了在运行时出现类型转换异常。这样,开发者在编写代码时就能发现并修复类型相关的错误。

        3)代码复用:泛型增加了代码的灵活性和复用性。通用的算法或数据结构可以用于处理各种类型的数据,而不需要为每种类型都编写相似的代码。

3、泛型语法

        在Java中,泛型使用尖括号(<>)来声明类型参数。例如:

public class Box<T> {private T value;public Box(T value) {this.value = value;}public T getValue() {return value;}
}

        在上面的例子中,Box 是一个泛型类,其中 表示类型参数。T 是一个占位符,它会在实例化时被具体的类型替代。

        使用泛型的例子如下:

// 创建一个存储整数的Box对象
Box<Integer> integerBox = new Box<>(42);
int intValue = integerBox.getValue(); // 不需要进行类型转换,编译器已经检查过了// 创建一个存储字符串的Box对象
Box<String> stringBox = new Box<>("Hello, Generics!");
String stringValue = stringBox.getValue(); // 同样不需要进行类型转换

        在这个例子中,Box 类被实例化两次,分别用于存储整数和字符串,而不需要为每一种类型编写不同的类。这样就实现了通用性和代码复用。泛型的强大之处在于它可以适用于各种数据类型,提高了代码的灵活性和可维护性。


 二、为什么需要泛型

1、引出泛型:

        我们可以通过引入泛型来实现一个通用的类,使其能够存放任何类型的数据,并提供方法获取数组中指定下标的值。以下是一个简单的示例:

public class GenericArray<T> {private T[] array;// 构造方法,初始化泛型数组public GenericArray(int size) {// 创建泛型数组的通用方式是使用 Object 类型的数组,然后进行强制类型转换this.array = (T[]) new Object[size];}// 设置数组指定下标的值public void set(int index, T value) {array[index] = value;}// 获取数组指定下标的值public T get(int index) {return array[index];}public static void main(String[] args) {// 创建一个存放整数的数组GenericArray<Integer> intArray = new GenericArray<>(5);intArray.set(0, 42);intArray.set(1, 15);// 获取数组中指定下标的值int valueAtIndex1 = intArray.get(1);System.out.println("Value at index 1: " + valueAtIndex1);// 创建一个存放字符串的数组GenericArray<String> stringArray = new GenericArray<>(3);stringArray.set(0, "Hello");stringArray.set(1, "Generics");// 获取数组中指定下标的值String valueAtIndex0 = stringArray.get(0);System.out.println("Value at index 0: " + valueAtIndex0);}
}

        在这个示例中,GenericArray 类是一个泛型类,它使用类型参数 T 表示数组中元素的类型。构造方法中创建了一个 Object 类型的数组,然后进行强制类型转换。通过泛型,我们可以在使用该类时指定存放的数据类型,从而实现了通用性。

 2、泛型擦除

        Java中的擦除机制(Type Erasure)是指在编译时期,泛型类型信息会被擦除,T这个占位符会被全部替换成为Object或者是它的上界,而在运行时,Java虚拟机将不再保留泛型类型的具体信息。这是为了向后兼容性,因为在引入泛型之前的Java版本中,是没有泛型概念的。

        1)类型参数擦除:在编译时,泛型类型的类型参数将被擦除,替换为Object,例如对于List,编译后的代码中的类型信息将被替换为List<Object>。

        2)泛型数组限制:由于擦除机制,不能直接创建泛型数组。例如,以下代码是非法的:

List<String>[] arrayOfLists = new ArrayList<String>[10]; 

        编译器会发出警告,因为在运行时,无法获得泛型数组的确切类型信息。通常,可以使用原始类型的数组,然后进行强制类型转换,但这可能导致运行时异常。

擦除机制的主要目标是确保与没有泛型的旧代码的兼容性,并允许在运行时处理泛型代码。然而,擦除也带来了一些挑战,尤其是在编写需要在运行时访问泛型类型信息的代码时。在这种情况下,通常需要使用反射或其他技术来处理擦除后的类型。

在Java中,T[] ts = new T[5]; 是不允许的,因为直接创建泛型数组是违反Java语言规范的。泛型在Java中是通过擦除实现的,即在运行时泛型信息会被擦除,编译器会将泛型类型替换为原始类型或其边界。

T[] ts = new T[5];

        那么在运行时,它实际上会被替换为:

Object[] ts = new Object[5];

        这似乎是合理的,但问题在于泛型的目的是提供类型安全性,而直接使用 Object[] 会失去泛型的好处。你可以轻松地将任意对象放入 Object[] 中,而不会得到编译器的警告。这就违背了使用泛型的初衷,因为泛型的目标是在编译时提供更严格的类型检查。

因此,为了维护类型安全性,Java禁止直接创建泛型数组。相反,你应该使用集合类或其他类型安全的数据结构,或者在需要时使用非泛型数组,并在运行时进行必要的类型转换。

3、为什么不能实例化泛型数组

        因为一个T类型的数组也就是T[] array = new T[];或者:

class MyArray<T> {
public T[] array = (T[])new Object[10];
public T getPos(int pos) {
return this.array[pos];
}
public void setVal(int pos,T val) {
this.array[pos] = val;
}
public T[] getArray() {
return array;
}
}
public static void main(String[] args) {
MyArray<Integer> myArray1 = new MyArray<>();
Integer[] strings = myArray1.getArray();
}

        泛型擦除之后,这个泛型又没有设置上界(泛型的上界我们一会介绍),T占位符全部被替换成Object,也就是可以是任意类型,这个数组里面可以存放任意类型,编译器认为这是不安全的,而且数组存放什么内容是运行时决定的,编译器无法对其进行限制,所以Java的编译器不允许程序员做这样危险的操作。

        尤其是这行代码:

Integer[] strings = myArray1.getArray();

        这个数组里面可能存放了任意类型的数据,而这个时候又要把这个数组赋值给一个Integer类型的数组,编译器一定报错。


三、泛型上界

        在定义泛型类时,有时需要对传入的类型变量做一定的约束,可以通过类型边界来约束。
1、泛型语法
        
class 泛型类名称<类型形参 extends 类型边界> {
...
}

 2、实例

public class MyArray<E extends Number> {
...
}
        只接受 Number 的子类型作为 E 的类型实参
MyArray<Integer> l1; // 正常,因为 Integer 是 Number 的子类型
MyArray<String> l2; // 编译错误,因为 String 不是 Number 的子类型

        如果没有指定类型边界 E,可以视为 E extends Object。


四、泛型方法

        泛型方法是一种在方法级别使用泛型的方式,允许在方法中使用具有独立类型参数的泛型。通过泛型方法,我们可以编写更灵活、通用的代码,而不受限于整个类的泛型类型参数。下面是一个简单的泛型方法的例子:

public class GenericMethodExample {// 泛型方法,接收一个数组并返回数组中的最大值//这里是泛型的上界代表着这个方法必须实现Comparable接口public static <T extends Comparable<T>> T findMax(T[] array) {if (array == null || array.length == 0) {return null;  // 返回 null 表示数组为空}T max = array[0];for (T element : array) {if (element.compareTo(max) > 0) {max = element;}}return max;}public static void main(String[] args) {// 使用泛型方法找到整数数组的最大值Integer[] intArray = {3, 7, 1, 9, 4};Integer maxInt = findMax(intArray);System.out.println("Max Integer: " + maxInt);// 使用泛型方法找到字符串数组的最大值String[] stringArray = {"apple", "orange", "banana", "grape"};String maxString = findMax(stringArray);System.out.println("Max String: " + maxString);}
}

        findMax()方法是一个泛型方法,使用 > 来声明泛型类型参数。这表示传入的类型 T 必须实现 Comparable 接口(泛型的上界),以便进行比较。

方法使用了泛型类型参数

        T 来定义数组和返回值的类型。这样,findMax 方法可以接受不同类型的数组,包括整数、浮点数、字符串等。

        在 main 方法中,我们分别使用 findMax 方法找到整数数组和字符串数组的最大值。

通过使用泛型方法,我们可以避免为每个类型都编写一个专门的方法,使代码更加灵活和通用。泛型方法通常在集合类、算法类等地方得到广泛应用。


 总结

        泛型的学习主要是为了我们更好地学习数据结构,所以这部分的内容大家会用就可以,主要通过上手实践来解决问题。

相关文章:

【JavaSE】泛型

系列文章目录 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 系列文章目录前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 前言 学习泛型之前请大家先详细地了解一下&#xff0c;关于Java…...

APS(高级计划与调度系统)难度超高,ERP在它面前就是弟弟。

一、APS定义和功能模块 APS系统是Advanced Planning and Scheduling System&#xff08;高级计划与调度系统&#xff09;的缩写。它是一种计划和调度管理软件系统&#xff0c;旨在帮助企业优化生产计划和资源调度&#xff0c;提高生产效率和响应能力。 APS系统利用先进的算法和…...

ArmV8架构

Armv8/armv9架构入门指南 — Armv8/armv9架构入门指南 v1.0 documentation 上面只是给了一个比较好的参考文档 其他内容待补充...

[论文笔记] Open-sora 2、视频数据集介绍 MSR-VTT

MSR-VTT COVE - Computer Vision Exchange 论文参考:https://www.microsoft.com/en-us/research/wp-content/uploads/2016/06/cvpr16.msr-vtt.tmei_-1.pdf 用于视频理解的大规模视频基准,特别是将视频翻译为文本的新兴任务。这是通过从商业视频搜索引擎收集 257 个热门查询…...

【Windows 常用工具系列 14 -- windows 网络驱动映射】

文章目录 windows 网络驱动映射 windows 网络驱动映射 映射网络驱动器的意思是将局域网中的某个目录映射成本地驱动器号。 在windows上将服务器目录映射到本地盘&#xff1a; 进入到服务器执行下面命令既可以看到对应的 IP地址&#xff1a; 将对应的IP地址填入上图中。 映…...

Java中使用Jsoup实现网页内容爬取与Html内容解析并使用EasyExcel实现导出为Excel文件

场景 Pythont通过request以及BeautifulSoup爬取几千条情话&#xff1a; Pythont通过request以及BeautifulSoup爬取几千条情话_爬取情话-CSDN博客 Node-RED中使用html节点爬取HTML网页资料之爬取Node-RED的最新版本&#xff1a; Node-RED中使用html节点爬取HTML网页资料之爬…...

闫震海:腾讯音乐空间音频技术的发展和应用 | 演讲嘉宾公布

一、3D 音频 3D 音频分论坛将于3月27日同期举办&#xff01; 3D音频技术不仅能够提供更加真实、沉浸的虚拟世界体验&#xff0c;跨越时空的限制&#xff0c;探索未知的世界。同时&#xff0c;提供更加丰富、立体的情感表达和交流方式&#xff0c;让人类能够更加深入地理解彼此&…...

Java基础 - 6 - 面向对象(二)

Java基础 - 6 - 面向对象&#xff08;一&#xff09;-CSDN博客 二. 面向对象高级 2.1 static static叫做静态&#xff0c;可以修饰成员变量、成员方法 2.1.1 static修饰成员变量 成员变量按照有无static修饰&#xff0c;分为两种&#xff1a;类变量、实例变量&#xff08;对象…...

SpringCloud-MQ消息队列

一、消息队列介绍 MQ (MessageQueue) &#xff0c;中文是消息队列&#xff0c;字面来看就是存放消息的队列。也就是事件驱动架构中的Broker。消息队列是一种基于生产者-消费者模型的通信方式&#xff0c;通过在消息队列中存放和传递消息&#xff0c;实现了不同组件、服务或系统…...

代码随想录算法训练营第三十八天|509. 斐波那契数、70. 爬楼梯、746. 使用最小花费爬楼梯

509. 斐波那契数 刷题https://leetcode.cn/problems/fibonacci-number/description/文章讲解https://programmercarl.com/0509.%E6%96%90%E6%B3%A2%E9%82%A3%E5%A5%91%E6%95%B0.html#%E7%AE%97%E6%B3%95%E5%85%AC%E5%BC%80%E8%AF%BE视频讲解https://www.bilibili.com/video/BV…...

[python] 代码工具箱

在 Python 3 的开发过程中&#xff0c;有一些小而实用的工具包可以帮助减轻开发负担&#xff0c;提升工作效率。这些工具包通常专注于解决特定问题或提供特定功能&#xff0c;使代码更简洁和可维护。以下是一些常用的工具包&#xff0c;可以简化开发过程&#xff1a; backoff&a…...

Linux——网络基础

计算机网络背景 网络发展 独立模式: 计算机之间相互独立 在早期的时候&#xff0c;计算机之间是相互独立的&#xff0c;此时如果多个计算机要协同完成某种业务&#xff0c;那么就只能等一台计算机处理完后再将数据传递给下一台计算机&#xff0c;然后下一台计算机再进行相应…...

Vue:双token无感刷新

文章目录 初次授权与发放Token&#xff1a;Access Token的作用&#xff1a;Refresh Token的作用&#xff1a;无感刷新&#xff1a;安全机制&#xff1a;后端创建nest项目AppController 添加login、refresh、getinfo接口创建user.dto.tsAppController添加模拟数据 前端Hbuilder创…...

实现一个作用域插槽的场景

vue项目中&#xff0c;插槽slot有三种分别是&#xff1a;默认插槽、具名插槽、作用域插槽。默认插槽和具名插槽在平时的开发中用的比较多&#xff0c;作用域插槽用的相对较少&#xff0c;以前我对作用域插槽不是很理解&#xff0c;现在理解了一下。下面通过代码来实现一个作用域…...

Qt QPainter的使用方法

重点&#xff1a; 1.QPainter在QWidget窗口的paintEvent中使用。 2.QPainter通常涉及到设置画笔、设置画刷、绘图&#xff08;QPen、QBrush、drawxx&#xff09;三个流程。 class Widget : public QWidget {Q_OBJECTprotected:void paintEvent(QPaintEvent *event) Q_DEC…...

低代码:数智化助力新农业发展

随着科技的飞速发展和数字化转型的深入推进&#xff0c;低代码开发平台正逐渐成为软件开发的热门话题。尤其在农业领域&#xff0c;低代码技术为传统农业注入了新的活力&#xff0c;助力新农业实现高效、智能的发展。 低代码开发平台的概念与特点 随着科技的飞速发展&#xff0…...

3d模型怎么镜像?3d模型镜像的步骤---模大狮模型网

在3D建模软件中&#xff0c;对3D模型进行镜像操作通常是指沿着某个轴线(如X、Y、Z轴)进行镜像翻转&#xff0c;使模型在该轴线的一侧产生对称的镜像效果。以下是在常见的3D建模软件中对3D模型进行镜像的一般步骤&#xff1a; 3d模型镜像步骤&#xff1a; 选择模型&#xff1a;…...

笔记本hp6930p安装Android-x86补记

在上一篇日记中&#xff08;笔记本hp6930p安装Android-x86避坑日记-CSDN博客&#xff09;提到hp6930p安装Android-x86-9.0&#xff0c;无法正常启动&#xff0c;本文对此再做尝试&#xff0c;原因是&#xff1a;Android-x86-9.0不支持无线网卡&#xff0c;需要在BIOS中关闭WLAN…...

为什么MySQL中多表联查效率低,连接查询实现的原理是什么?

MySQL中多表联查效率低的原因主要涉及到以下几个方面&#xff1a; 数据量大: 当多个表通过连接查询时&#xff0c;如果这些表的数据量很大&#xff0c;那么查询就需要处理更多的数据&#xff0c;这自然会降低查询效率。 连接操作复杂性: 连接查询需要对参与连接的每个表中的数…...

从下一代车规MCU厘清存储器的发展(2)

目录 1.概述 2.MCU大厂的选择 2.1 瑞萨自研STT-MRAM 2.2 ST专注PCM 2.3 英飞凌和台积电联手RRAM 2.4 NXP如何计划eNVM 3.小结 1.概述 上篇文章&#xff0c;我们简述了当前主流的存储器技术&#xff0c;现在我们来讲讲各大MCU大厂的技术选择 2.MCU大厂的选择 瑞萨日…...

Redis(理论版)

Redis 1.Redis是什么 Redis其实就是一个数据库&#xff0c;它是一个文档型数据库&#xff08;非关系型数据库&#xff09;,而mysql是一个关系型数据库。它是一个开源的、基于内存的高性能键值存储数据库&#xff0c;支持多种数据结构&#xff0c;广泛用于缓存、消息队列、应用…...

【NR 定位】3GPP NR Positioning 5G定位标准解读(四)

目录 前言 6 Signalling protocols and interfaces 6.1 支持定位操作的网络接口 6.1.1 通用LCS控制平面架构 6.1.2 NR-Uu接口 6.1.3 LTE-Uu接口 6.1.4 NG-C接口 6.1.5 NL1接口 6.1.6 F1接口 6.1.7 NR PC5接口 6.2 终端协议 6.2.1 LTE定位协议&#xff08;LPP&#x…...

Docker容器化解决方案

什么是Docker&#xff1f; Docker是一个构建在LXC之上&#xff0c;基于进程容器的轻量级VM解决方案&#xff0c;实现了一种应用程序级别的资源隔离及配额。Docker起源于PaaS提供商dotCloud 基于go语言开发&#xff0c;遵从Apache2.0开源协议。 Docker 自开源后受到广泛的关注和…...

Docker安装+基础命令

一、检测、配置安装环境 &#xff08;1&#xff09;查看linux版本&#xff0c;是否符合>centos 7 &#xff08;2&#xff09;查看网络是否通畅 &#xff08;3&#xff09;安装gcc&#xff0c;gcc-c编译器 &#xff08;4&#xff09;安装device-mapper-persistent-data和lvm2…...

构建高性能Linux Virtual Server(LVS)集群

目录 引言 一、集群的基本理论 &#xff08;一&#xff09;什么是集群 &#xff08;二&#xff09;集群的分类 &#xff08;三&#xff09;LB Cluster 负载均衡集群 1.按实现方式划分 2.按协议层划分 &#xff08;四&#xff09;HA 高可用集群实现 二、LVS简介 &…...

Linux:线程的概念

个人主页 &#xff1a; 个人主页 个人专栏 &#xff1a; 《数据结构》 《C语言》《C》《Linux》 文章目录 前言一、线程的概念线程代码的简单示例 总结 前言 本文是对于线程概念的知识总结 一、线程的概念 在课本上&#xff0c;线程是比进程更轻量级的一种指向流 或 线程是在…...

如何在jupyter notebook 中下载第三方库

在anconda 中找到&#xff1a; Anaconda Prompt 进入页面后的样式&#xff1a; 在黑色框中输入&#xff1a; 下载第三方库的命令 第三方库&#xff1a; 三种输入方式 标准保证正确 pip instsall 包名 -i 镜像源地址 pip install pip 是 Python 包管理工具&#xff0c;…...

Linux下du命令和df命令的使用

du命令作用是估计文件系统的磁盘已使用量&#xff0c;常用于查看文件或目录所占磁盘容量。df命令是统计磁盘使用情况&#xff0c;可以用来查看磁盘已被使用多少空间和还剩余多少空间。du命令语法du [选项] [文件或目录名称]参数&#xff1a;-a&#xff1a;--all&#xff0c; 列…...

AIGC笔记--条件自回归Transformer的搭建

1--概述 1. 自回归 TransFormer 规定Token只能看到自身及前面的Token&#xff0c;因此需生成一个符合规定的Attention Mask&#xff1b;&#xff08;代码提供了两种方式自回归Attention Mask的定义方式&#xff09;&#xff1b; 2. 使用Cross Attention实现条件模态和输入模态之…...

数据结构->链表分类与oj(题),带你提升代码好感

✅作者简介&#xff1a;大家好&#xff0c;我是橘橙黄又青&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;橘橙黄又青-CSDN博客 1.&#x1f34e;链表的分类 前面我们学过顺序表&#xff0c;顺序表问题&#xff1a; …...