Java泛型理解

news2024/11/19 17:25:51

什么是泛型?

我们都知道 Java 中有形参和实参之分,是在定义函数名和函数体的时候使用的参数,目的是用来接收调用该函数时传入的参数,其本身没有确定的值。在调用函数时,实参将赋值给形参。

而泛型是一种参数化的类型(可以理解为类型形参),它允许在定义类、接口时通过一个「标识」表示类中某个属性的类型或者是某个方法的返回值或参数的类型。这个类型参数将在使用时(例如,继承或实现这个接口、创建对象或调用方法时)确定(即实际传入的类型参数,也称为类型实参)。好处是增强代码的安全性。

以 Java 中的集合为例,如果不指定泛型,那么传入集合中的元素就可以是任意类型的,这就可能带来一些问题,比如我取出集合中的某个元素后,可能要进行「类型转换」,但是如果不指定泛型,那么我是无法知道集合中的元素的什么类型的(默认是 Object 类型),就可能带来 ClassCastException 异常。

泛型的使用场景?

  • 自定义通用返回结果 CommonResult<T> 通过参数 T 可根据具体的返回类型动态指定结果的数据类型
  • DAO 层、自定义的通用泛型方法
  • 比较器(Comparable、Comparator)

泛型的使用方式有哪几种?

泛型一般有三种使用方式:泛型类、泛型接口、泛型方法。

泛型方法的一个例子:

// 创建不同类型的数组:  
Integer[] intArray = {1, 2, 3};  
String[] stringArray = {"hello", "world"};  
printArray(intArray);  
printArray(stringArray);

public static <E> void printArray(E[] inputArray) {  
        for (E element : inputArray) {  
            System.out.printf("%s ", element);  
        }  
        System.out.println();  
}

个人认为使用泛型方法的好处有二:

  1. 允许方法传入多种类型的参数,提高了方法的可扩展性。
  2. 允许方法使用和类不同的泛型参数,比如上面的泛型方法使用的是 E,而该方法所在的类使用的可以是 T。

什么是泛型擦除?为什么要擦除?

泛型擦除就是指 Java 程序在编译期间所有的泛型信息都会被擦除,也就是「类型擦除」。

类型擦除的主要过程如下:1.将所有的泛型参数用其最左边界(最顶级的父类型)类型替换。2.移除所有的类型参数。

比如编译器会在编译期间将泛型 T 擦除为 Object 类型,或将 T extends xxx 擦除为其限定类型 xxx。

总结:泛型本质上还是「编译时行为」。为了保证引入泛型机制但不创建新的类型,减少 JVM 的运行开销,编译器会通过擦除将泛型类转换为一般类。

举个例子来证明泛型是「编译时行为」:

List<Integer> list = new ArrayList<>();  
list.add(2);  
// 1. 编译期间直接添加与泛型参数类型不同元素会报错,因为有「泛型校验」
list.add("aa");  
// 2. 运行期间通过反射添加却是可以的
Class<? extends List> clazz = list.getClass();  
Method add = clazz.getDeclaredMethod("add", Object.class);  
add.invoke(list, "kl");  
System.out.println(list);

也正是由于泛型擦除的问题,下面的「方法重载会报错」:

public static void test(List<String> list1) {  
    System.out.println(1);  
}  
  
public static void test(List<Integer> list1) {  
    System.out.println(1);  
}

因为擦除后大家都是 List 了,默认里面存的就是 Object 类型的元素。

通配符

由于如果泛型类型只能是固定的,在某些场景下使用起来不够灵活。

使用通配符可以解决泛型无法协变的问题。

协变指的就是如果 Child 是 Parent 的子类,那么List<Child> 也应该是List<Parent> 的子类,但是泛型是不支持的。

无界通配符

List<?>List 有区别吗?当然有!

List<?> list 表示 list 是持有某种特定类型的 List,但是不知道具体是哪种类型。因此,我们添加元素进去的时候会报错。

List list 表示 list 是持有的元素的类型是 0bject,因此可以添加任何类型的对象,只不过编译器会有警告信息。

List<?> list = new ArrayList<>( );
list.add("sss");//报错
List list2 = new ArrayList<>();
list2. add("sss");//警告信息

上/下边界通配符

在使用泛型的时候,我们还可以为传入的泛型类型实参进行上下边界的限制,如:类型实参只准传入某种类型的父类或某种类型的子类。

上边界通配符 extends 要求传入的类型实参必须是指定类型或其子类。

举个例子:

// 限制必须是 Person 类或其子类
<? extends Person>

下边界通配符 super 则要求传入的类型实参必须是指定类型或其父类。

// 限制必须是 Person 类或其父类
<? super Person>

使用 <? extends xxx><? super extends xxx> 需要注意的点

G<Father> 和 G<Son> 没有继承关系。

image.png

这块可能不太好理解,具体可以看宋红康老师 JavaSE 的视频。

泛型中的坑

1.当泛型遇到 catch

泛型的类型参数不能用在 Java 异常处理的 catch 语句中。因为异常处理是由 JVM 在运行时刻来进行的。由于类型信息被擦除,JVM 是无法区分两个异常类型MyException<String>MyException<Integer>

2.当泛型类内包含静态变量

public class StaticTest{
    public static void main(String[] args){
        GT<Integer> gti = new GT<Integer>();
        gti.var=1;
        GT<String> gts = new GT<String>();
        gts.var=2;
        System.out.println(gti.var);
    }
}
class GT<T>{
    public static int var=0;
    public void nothing(T x){}
}

以上代码输出结果为:2!

由于经过类型擦除,所有的泛型类实例都关联到同一份字节码上,它们的静态变量是共享的。

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

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

相关文章

【云备份】

文章目录 [toc] 1 :peach:云备份的认识:peach:1.1 :apple:功能了解:apple:1.2 :apple:实现目标:apple:1.3 :apple:服务端程序负责功能:apple:1.4 :apple:服务端功能模块划分:apple:1.5 :apple:客户端程序负责功能:apple:1.6 :apple:客户端功能模块划分:apple: 2 :peach:环境搭建…

Cocos Creator3.8 实战问题(四)巧用九宫格图像拉伸

一、为什么要使用九宫格图像拉伸 相信做过前端的同学都知道&#xff0c;ui &#xff08;图片&#xff09;资源对包体大小和内存都有非常直接的影响。 通常ui 资源都是图片&#xff0c;也是最占资源量的资源类型&#xff0c;游戏中的ui 资源还是人机交互的最重要的部分&#xff…

【Java】main方法的深入理解

目录 深入理解 main 方法 public static void main(String[] args) ​编辑示例代码&#xff1a; 编译运行&#xff08;String[] args&#xff09;&#xff1a; main 方法的注意事项 示例代码&#xff1a; 深入理解 main 方法 public static void main(String[] args) mai…

HTTP协议,请求响应

、概述 二、HTTP请求协议 三、HTTP响应协议 四、请求数据 1.简单实体参数 RequestMapping("/simpleParam")public String simpleParam(RequestParam(name "name" ,required false ) String username, Integer age){System.out.println (username "…

基于Java的火车高铁订票购票系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作…

PowerShell 内网不能直接安装SqlServer模块的处理办法

PowerShell 内网不能直接安装SqlServer模块的处理办法 文章目录 下载sqlserver module安装sqlserver module导入和验证sqlserver 模块推荐阅读 下载sqlserver module 首先先将sqlserver.nupkg下载到本地&#xff0c;我是放到了C:\windows\system32目录下。 PowerShell Galler…

2023年【电工(初级)】最新解析及电工(初级)新版试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 电工&#xff08;初级&#xff09;最新解析是安全生产模拟考试一点通总题库中生成的一套电工&#xff08;初级&#xff09;新版试题&#xff0c;安全生产模拟考试一点通上电工&#xff08;初级&#xff09;作业手机同…

采访|使用过ChatGPT的同学在与人工智能对话后的体验

“我能够回答各种问题&#xff0c;包括但不限于有关互联网、科技、娱乐、教育等领域的知识。我不会感到疲倦或分心&#xff0c;也没有情绪或偏见。”这是ChatGPT对自己的描述。ChatGPT拥有从巨大语料库中学习、理解自然语言文本的强大功能。强大的对话、理解能力使得它仅仅用了…

ArcGIS Engine:报错无法嵌入互操作类型“ESRI.ArcGIS.Geometry.EnvelopeClass”。请改用适用的接口。

此错误是由于尝试直接实例化ArcGIS COM组件的某些互操作类引起的。这在.NET Framework 4及更高版本中是不被推荐的。 为了解决此问题&#xff0c;你需要确保在工程的引用中将ArcGIS的互操作类型设置为“不嵌入”。 按照以下步骤操作&#xff1a; 在解决方案资源管理器中找到…

网络爬虫指南

一、定义 网络爬虫&#xff0c;是按照一定规则&#xff0c;自动抓取网页信息。爬虫的本质是模拟浏览器打开网页&#xff0c;从网页中获取我们想要的那部分数据。 二、Python为什么适合爬虫 Python相比与其他编程语言&#xff0c;如java&#xff0c;c#&#xff0c;C&#xff…

如何使用百度“云一朵”来分析PDF文件

PDF 文件是一种常见的文件格式&#xff0c;用于存储文档、图像和其他内容。在许多情况下&#xff0c;我们需要对 PDF 文件进行分析&#xff0c;以提取其中的信息。百度“云一朵”提供了一个 PDF 分析 API&#xff0c;可以帮助我们轻松地对 PDF 文件进行分析。 在本博客文章中&…

第15届蓝桥STEMA测评真题剖析-2023年8月20日Scratch编程中级组

[导读]&#xff1a;超平老师的《Scratch蓝桥杯真题解析100讲》已经全部完成&#xff0c;后续会不定期解读蓝桥杯真题&#xff0c;这是Scratch蓝桥杯真题解析第155讲。 第15届蓝桥第1次STEMA测评&#xff0c;这是2023年8月20日举办的STEMA&#xff0c;比赛仍然采取线上形式。这…

Java基础---第十五篇

系列文章目录 文章目录 系列文章目录一、红黑树有哪几个特征?二、说说你平时是怎么处理 Java 异常的三、说说深拷贝和浅拷贝?一、红黑树有哪几个特征? 紧接上个问题,面试官很有可能会问红黑树,下面把红黑树的几个特征列出来: 二、说说你平时是怎么处理 Java 异常的 try…

Ubuntu22.04 交叉编译gcc9.5 for arm

一、准备 环境&#xff1a;ubuntu22.04为刚刚安装&#xff0c;未安装gcc等包 vi ~/.bashrc输入 export PATH$PATH:/opt/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf/bin 保存,reboot 安装&#xff1a; sudo apt install cmake sudo apt install gawk sudo apt instal…

【go语言】方法

go的方法是一种作用在接收者&#xff08;某种类型的变量&#xff0c;不能是接口和指针&#xff09;上的特殊函数 方法的声明 // 类型方法接收者是值类型 func (t TypeName) MethodName (ParamList ) (Returnlist) {//method body }// 类型方法接收者是指针 func (t *TypeName…

A股上市公司数字技术创新与企业高质量发展 企业全要素生产率-5种测算方法 2000-2022

数据简介&#xff1a;全要素生产率(TFP&#xff09;也可以称之为系统生产率。指生产单位&#xff08;主要为企业&#xff09;作为系统中的各个要素的综合生产率&#xff0c;以区别于要素生产率&#xff08;如技术生产率&#xff09;。测算公式为&#xff1a;全要素生产率产出总…

解决nvm切换node版本失败的终极办法-秒杀网上99%的水文

nvm是一款强大的node多版本管理器&#xff0c;可以轻易选择你需要的node版本&#xff0c;这对win7平台简直就是超好的福音&#xff1a;可以突破node 14.15以上的安装限制。 但是nvm安装有一个巨大的坑点&#xff1a;nvm use 版本号以后&#xff0c;并没有生效&#xff0c;nvm …

2023年【R2移动式压力容器充装】模拟考试及R2移动式压力容器充装模拟考试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 R2移动式压力容器充装模拟考试考前必练&#xff01;安全生产模拟考试一点通每个月更新R2移动式压力容器充装模拟考试题题目及答案&#xff01;多做几遍&#xff0c;其实通过R2移动式压力容器充装操作证考试很简单。 1…

概率密度函数,概率分布函数

概率密度函数&#xff1a;描述信号的取值在某个确定的取值点附近的概率的函数&#xff1b;概率分布函数的导数。 以幅值大小为横坐标&#xff0c;以每个幅值间隔内出现的概率为纵坐标进行统计分析。反映了信号落在不同幅值强度区域内的概率情况。 直方图&#xff1a;对每个幅…