Java泛型理解

什么是泛型?

我们都知道 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.hqwc.cn/news/124251.html

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

相关文章

Linux进程概念(上)

冯诺依曼体系结构 这里谈论的体系结构指的是计算机组成 常见的计算机&#xff0c;如笔记本&#xff0c;不常见的计算机&#xff0c;如服务器&#xff0c;大部分都遵守冯诺依曼体系 计算机&#xff0c;都由一个个的硬件组件组成 输入单元&#xff1a;如键盘&#xff0c;…

多源最短路径的原理及C++实现

时间复杂度 O(n3),n是端点数。 核心代码 template<class T, T INF 1000 * 1000 * 1000> class CNeiBoMat { public: CNeiBoMat(int n, const vector<vector<int>>& edges,bool bDirectfalse,bool b1Base false) { m_vMat.assign(n, vector<…

MySQL5.7版本与8.0版本在Ubuntu(WSL环境)系统安装

目录 前提条件 1. MySQL5.7版本在Ubuntu&#xff08;WSL环境&#xff09;系统安装 1. 1 下载apt仓库文件 1.2 配置apt仓库 1.3 更新apt仓库的信息 1.4 检查是否成功配置MySQL5.7的仓库 5. 安装MySQL5.7 1.6 启动MySQL 1.7 对MySQL进行初始化 1.7.1 输入密码 …

Android 面试经历复盘整理~

此次面试一共4面4小时&#xff0c;中间只有几分钟间隔。对持续的面试状态考验还是蛮大的。 关于面试的心态&#xff0c;保持悲观的乐观主义心态比较好。面前做面试准备时保持悲观&#xff0c;尽可能的做足准备。面后积极做复盘&#xff0c;乐观的接受最终结果。 切忌急于下结论…

IDM(Internet Download Manager)2024中文版下载工具软件

IDM&#xff08;Internet Download Manager&#xff09;&#xff1a;功能强大&#xff0c;下载速度快&#xff0c;支持多线程下载&#xff0c;下载过程中遇到突然断电等情况&#xff0c;可以进行断点续传&#xff0c;很多人因此而首选IDM。 优点&#xff1a; &#xff08;1&a…

嵌入式Linux应用开发-驱动大全-同步与互斥③

嵌入式Linux应用开发-驱动大全-同步与互斥③ 第一章 同步与互斥③1.4 Linux锁的介绍与使用1.4.1 锁的类型1.4.1.1 自旋锁1.4.1.2 睡眠锁 1.4.2 锁的内核函数1.4.2.1 自旋锁1.4.2.2 信号量1.4.2.3 互斥量1.4.2.4 semaphore和 mutex的区别 1.4.3 何时用何种锁1.4.4 内核抢占(pree…

2023蓝帽杯半决赛电子取证+CTF部分题解

文章目录 电子取证123456789101112131415 CTFWeb | MyLinuxBotWeb | AirticleShareCrypto | ezrsaPwn | AdminPwn | uafmisc|排排坐吃吃果果 电子取证 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 CTF Web | MyLinuxBot Web | AirticleShare import requests import times reques…

imgui开发笔记<1>、ubuntu环境下快速应用

去这个链接下载imgui源码&#xff08;在此之前需要安装opengl glfw3等等&#xff09;&#xff1a; sudo apt-get install libglfw3-dev https://github.com/ocornut/imgui 我这里源码下载到/home/temp/imgui目录下&#xff0c;咱们不需要编译源码成库&#xff0c;而是直接将下…

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

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

免杀对抗-DLL劫持免杀

C&Py-DLL劫持-语言-调用加载 1.使用visual studio创建项目 2.将文件名重命名为.c后缀 3.将如下加载器代码生成dll文件 加载器代码&#xff1a; #include "pch.h" #include <Windows.h> #include <stdio.h> #include <string.h>#pragma comment…

ARM-day2

1、1到100累加 .text .global _start_start:MOV r0, #1ADD r1,r0, #1fun:ADD r0,r0,r1ADD r1,r1, #1cmp r1, #0x65moveq PC,LRbl funstop:b stop.end2、思维导图

ili9431液晶 tft_espi图形库演示 时钟、天气、滚动、气象图标

米思齐tft_spi模块库演示程序。心知天气、阿里云时钟、WiFi信号强度检测、1分钟滚屏、更新天气时间为15分钟、加入天气图标。更新天气次数。断网检测 。此程序为tft_eSPI图形库演示、如感觉好可以自行优化。 ili9431tft_espi库是用于ESP32和ESP8266芯片的TFT LCD驱动程序库&am…