18.Java泛型

news/2024/4/26 3:48:15/文章来源:https://blog.csdn.net/qq_45305209/article/details/130317649

目录


1. Java基本介绍

2. JDK下载安装及其环境配置

3. 一个简单的java程序

4. Eclipse基本使用、数据类型、运算符

5. 控制语句(if、switch、for、while、foreach)

6. Java数组

7. Java字符串对象(String|StringBuffer|StringBuilder|StringJoiner)

8. Java面向对象

9. 正则表达式

10. Java包装类

11. 日期与时间

12. 异常(throwable)

13. Java日志处理

14. Java集合框架(Collection)

15. Java多线程

16. Java IO数据流

17. Java枚举(Enum)

18. Java泛型

19. Java Class类和Java反射

20. JDBC操作数据库

21. Java网络通信

22. Java注解

23. Java 1.8新特性

24. Java函数式编程




泛型

泛型就是定义一种模板,既实现了编写一次,万能匹配,又通过编译器保证了类型安全:这就是泛型

泛型:编写模板代码来适应任意类型
(1)泛型的好处是使用时不必对类型进行强制转换,它通过编译器对类型进行检查
(2)使用泛型时,把泛型参数<T>替换为需要的class类型。ArrayList<String>、ArrayList<Number>等
注意泛型继承关系:可以把ArrayList<Integer>向上转型为List(T不变),但不能把ArrayList<Integer>向上转型为ArrayList<Number>(T不能变成父类)
可以省略编译器能自动推断出类型,例如:List<String> list = new ArrayList<>();(T省略)

(3)不指定泛型参数类型时,编译器会给出警告,且只能将视为Object类型
(4)可以在接口中定义泛型类型,实现此接口的类必须实现正确的泛型类型

(1)类型安全
泛型的主要目标是实现Java的类型安全。泛型可以使编译器知道一个对象的限定类型是什么,这样编译器就可以在一个高的程度上验证这个类型
(2)消除了强制类型转换,使得代码可读性好,减少了很多出错的机会

Java语言引入泛型的好处是安全简单。泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率

泛型实质
泛型实质:就是使程序员定义安全的类型
在没有出现泛型之前,Java也提供了对Object的引用“任意化”操作,这种任意化操作就是对Object引用进行“向下转型”及“向上转型”操作


定义泛型类

Object类为最上层的父类,通常为了使程序更为通用,设计程序时使传入的值与返回的值都以Object类型为主。以至于运行时将会发生ClassCastException异常

(1)编写泛型时,需要定义泛型类型<T>
(2)静态方法不能引用泛型类型<T>,必须定义其他类型(例如<K>)来实现静态泛型方法
(3)泛型可以同时定义多种类型。例如Map<K, V>

编写泛型类比普通类要复杂。通常来说,泛型类一般用在集合类中

类名<T>

T代表一个类型的名称

<T> T与T的区别

泛型描述
<T> T表示返回值是一个泛型,传递什么,就返回什么类型的数据
T表示限制传递的参数类型

常规用法

定义泛型类时声明多个类型

MutiOverClass<T1,T2>

MutiOverClass:泛型类名称
T1、T2为可能被定义的类型,这样在实例化指定类型的对象时就可以指定多个类型

1.基本类型

按照某种类型:String,来编写类。然后,标记所有的特定类型(String)

public class Pair {private String first;private String last;public Pair(String first, String last) {this.first = first;this.last = last;}public String getFirst() {return first;}public String getLast() {return last;}
}

2.泛型类

将基本类型String替换为T

public class Pair<T> {private T first;private T last;public Pair(T first, T last) {this.first = first;this.last = last;}public T getFirst() {return first;}public T getLast() {return last;}
}

3.静态方法

特别注意:泛型类型<T>不能用于静态方法,会导致编译错误,无法在静态方法的方法参数和返回类型上使用泛型类型T
可以在static修饰符后面加一个<T>,编译就能通过,但实际上,这个<T>和Pair<T>类型的<T>已经没有任何关系了

public class Pair<T> {private T first;private T last;public Pair(T first, T last) {this.first = first;this.last = last;}// 可以编译通过:public static <T> Pair<T> create(T first, T last) {return new Pair<T>(first, last);}
}

对于静态方法,可以单独改写为“泛型”方法,只需要使用另一个类型即可。对于上面的create()静态方法,应该把它改为另一种泛型类型

public class Pair<T> {private T first;private T last;public Pair(T first, T last) {this.first = first;this.last = last;}public T getFirst() { ... }public T getLast() { ... }// 静态泛型方法应该使用其他类型区分:public static <K> Pair<K> create(K first, K last) {return new Pair<K>(first, last);}
}

这样才能清楚地将静态方法的泛型类型实例类型的泛型类型区分开


4.多个泛型类型

泛型还可以定义多种类型。Pair类总是存储两个类型不一样的对象,就可以使用类型<T, K>

public class Pair<T, K> {private T first;private K last;public Pair(T first, K last) {this.first = first;this.last = last;}public T getFirst() { ... }public K getLast() { ... }
}

使用的时候,需要指出两种类型

Pair<String, Integer> p = new Pair<>("test", 123);

Java标准库的Map<K, V>就是使用两种泛型类型的例子。它对Key使用一种类型,对Value使用另一种类型


擦拭法

(1)泛型是一种类似“模板代码”的技术,不同语言的泛型实现方式不一定相同
(2)Java语言的泛型实现方式是擦拭法(Type Erasure)
擦拭法:虚拟机对泛型其实一无所知,所有的工作都是编译器做的

编译时

1.编译器看到的代码

public class Pair<T> {private T first;private T last;public T getFirst() {return first;}public T getLast() {return last;}
}

2.虚拟机执行的代码

虚拟机根本不知道泛型

public class Pair {private Object first;private Object last;public Object getFirst() {return first;}public Object getLast() {return last;}
}

也就是将<T>类型,转换为了Object

Java使用擦拭法实现泛型,导致
(1)编译器把类型<T>视为Object
(2)编译器根据<T>实现安全的强制转型


运行时

1.编译器写的代码

使用泛型的时候,编写的代码也是编译器看到的代码

Pair<String> p = new Pair<>("Hello", "world");
String first = p.getFirst();
String last = p.getLast();

2.虚拟机执行代码

而虚拟机执行的代码并没有泛型

Pair p = new Pair<>("Hello", "world");
String first = (String) p.getFirst();
String last = (String) p.getLast();

所以,Java的泛型是由编译器在编译时实行的,编译器内部永远把所有类型T视为Object处理,但是,在需要转型的时候,编译器会根据T的类型自动实行安全地强制转型


Java泛型的局限

Java泛型的实现方式(擦拭法),就知道了Java泛型的局限

局限一(不能是基本类型)

<T>不能是基本类型。例如int,因为实际类型是Object,Object类型无法持有基本类型

Pair<int> p = new Pair<>(1, 2); // compile error!

局限二(获取的Class实例都是同一个)

无法取得带泛型的Class

class Pair<T> {private T first;private T last;public Pair(T first, T last) {this.first = first;this.last = last;}public T getFirst() {return first;}public T getLast() {return last;}
}Pair<String> p1 = new Pair<>("Hello", "world");
Pair<Integer> p2 = new Pair<>(123, 456);
Class c1 = p1.getClass();
Class c2 = p2.getClass();
System.out.println(c1==c2); // true
System.out.println(c1==Pair.class); // true

因为T是Object,对Pair<String>和Pair<Integer>类型获取Class时,获取到的是同一个Class,也就是Pair类的Class
换句话说,所有泛型实例,无论T的类型是什么,getClass()返回同一个Class实例,因为编译后它们全部都是Pair<Object>


局限三(不能intanceof判断带泛型的Class)

无法判断带泛型的Class

Pair<Integer> p = new Pair<>(123, 456);
// Compile error:
if (p instanceof Pair<String>.class) {}

原因和前面一样,并不存在Pair<String>.class,而是只有唯一的Pair.class


局限四(不能实例化T类型)

不能实例化T类型

public class Pair<T> {private T first;private T last;public Pair() {// Compile error:first = new T();last = new T();}
}

上述代码无法通过编译,因为构造方法的两行语句

first = new T();
last = new T();

擦拭后实际上变成了:

first = new Object();
last = new Object();

这样一来,创建new Pair<String>()和创建new Pair<Integer>()就全部成了Object,显然编译器要阻止这种类型不对的代码

要实例化T类型,必须借助额外的Class<T>参数:

public class Pair<T> {private T first;private T last;public Pair(Class<T> clazz) {first = clazz.newInstance();last = clazz.newInstance();}
}

上述代码借助Class<T>参数并通过反射来实例化T类型,使用的时候,也必须传入Class<T>

Pair<String> pair = new Pair<>(String.class);

因为传入了Class<String>的实例,所以借助String.class就可以实例化String类型


不恰当的覆写方法(需要避开与Object的冲突)

有些时候,一个看似正确定义的方法会无法通过编译

public class Pair<T> {public boolean equals(T t){return this == t;}
}

这是因为,定义的equals(T t)方法实际上会被擦拭成equals(Object t),而这个方法是继承自Object的,编译器会阻止一个实际上会变成覆写的泛型方法定义
换个方法名,避开与Object.equals(Object)的冲突就可以成功编译

public class Pair<T> {public boolean same(T t) {return this == t;}
}

泛型继承

一个类可以继承自一个泛型类,使用的时候,因为子类IntPair并没有泛型类型,所以,正常使用即可

public class IntPair extends Pair<Integer> {}
IntPair ip = new IntPair(1 , 2); 

在继承了泛型类型的情况下,子类可以获取父类的泛型类型。例如:IntPair可以获取到父类的泛型类型Integer

class Pair<T> {private T first;private T last;public Pair(T first, T last) {this.first = first;this.last = last;}public T getFirst() {return first;}public T getLast() {return last;}
}class IntPair extends Pair<Integer> {public IntPair(Integer first, Integer last) {super(first, last);}
}Class<IntPair> clazz = IntPair.class;
Type t = clazz.getGenericSuperclass();
if(t instanceof ParameterizedType) {ParameterizedType pt = (ParameterizedType) t;// 可能有多个泛型类型Type[] types = pt.getActualTypeArguments();// 取第一个泛型类型Type firstType = types[0];Class<?> typeClass = (Class<?>) firstType;System.out.println(typeClass); // Integer
}

Java的类型系统结构
在这里插入图片描述

(1)Java的泛型是采用擦拭法实现
(2)擦拭法决定了泛型<T>

条件示例
①不能是基本类型int
②不能获取带泛型类型的ClassPair<String>.class
③不能判断带泛型类型的类型x instanceof Pair<String>
④不能实例化T类型new T()

(3)泛型方法要防止重复定义方法:public boolean equals(T obj)
(4)子类可以获取父类的泛型类型<T>

extends通配符(其子类或本身,下界)

<? extends Number>的泛型定义称之为上界通配符(Upper Bounds Wildcards),即把泛型类型T的上界限定在Number了,?代表的是Number或Number的子类

除了可以传入Pair<Integer>类型,我们还可以传入Pair<Double>类型,Pair<BigDecimal>类型等等,因为Double和BigDecimal都是Number的子类

class Pair<T> {private T first;private T last;public Pair(T first, T last) {this.first = first;this.last = last;}public T getFirst() {return first;}public T getLast() {return last;}
}public static void main(String[] args) {Pair<Integer> p = new Pair<>(123, 456);int n = add(p);System.out.println(n);
}static int add(Pair<? extends Number> p) {Number first = p.getFirst();Number last = p.getLast();return first.intValue() + last.intValue();
}

1.作用

如果考察Java标准库的java.util.List<T>接口,它实现的是一个类似“可变数组”的列表,主要功能包括

public interface List<T> {int size(); // 获取个数T get(int index); // 根据索引获取指定元素void add(T t); // 添加一个新元素void remove(T t); // 删除一个已有元素
}

2.限定T类型

定义泛型类型Pair<T>的时候,也可以使用extends通配符来限定T的类型

public class Pair<T extends Number> {}

非Number类型将无法通过编译

(1)使用类似<? extends Number>通配符作为方法参数时表示
方法内部可以调用获取Number引用的方法,例如:Number n = obj.getFirst();;
方法内部无法调用传入Number引用的方法(null除外),例如:obj.setFirst(Number n);。
即一句话总结:使用extends通配符表示可以读,不能写
(2)使用类似<T extends Number>定义泛型类时表示:泛型类型限定为Number以及Number的子类


super通配符(其父类或本身,下界)

Pair<? super Integer>表示,方法参数接受所有泛型类型为Integer或Integer父类的Pair类型


extends和super比较

extendssuper
作为方法参数允许调用读方法T get()获取T的引用,但不允许调用写方法set(T)传入T的引用(传入null除外)允许调用写方法set(T)传入T的引用,但不允许调用读方法T get()获取T的引用(获取Object除外)

一个是允许读不允许写,另一个是允许写不允许读


PECS(Producer Extends Consumer Super)原则

如果需要返回T,它是生产者(Producer),要使用extends通配符;如果需要写入T,它是消费者(Consumer),要使用super通配符
总结出一条规律“Producer Extends,Consumer Super”:返回T(读)用extends,写入T(写)super
(1)“Producer Extends”:如果需要一个只读List,用它来Producer T,那么使用? extends T
(2)“Consumer Super”:如果需要一个只写List,用它来Consumer T,那么使用? super T
(3)如果需要同时读取以及写入,那么就不能使用通配符了


无限定通配符

<? extends T>和<? super T>作为方法参数的作用。实际上,Java的泛型还允许使用无限定通配符(Unbounded Wildcard Type),即只定义一个?

<?>通配符既没有extends,也没有super,因此:

(1)不允许调用set(T)方法并传入引用(null除外)
(2)不允许调用T get()方法并获取T引用(只能获取Object引用)
既不能读,也不能写,只能做一些null判断

(1)使用类似<? super Integer>通配符作为方法参数时表示
方法内部可以调用传入Integer引用的方法,例如:obj.setFirst(Integer n);;
方法内部无法调用获取Integer引用的方法(Object除外),例如:Integer n = obj.getFirst();。
即使用super通配符表示只能写不能读
(2)使用extends和super通配符要遵循PECS原则
(3)无限定通配符<?>很少使用,可以用<T>替换,同时它是所有<T>类型的超类

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

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

相关文章

OFDM-LS信道估计 MMSE信道估计公式推导

假设ofdmN个子载波之间是完全正交的&#xff0c;即不考虑ICI影响&#xff0c;通过发送训练序列来实现信道估计。 其中&#xff0c;在推导6.8的时候&#xff0c;需要将6.6先拆解一下。 X − 1 Y X − 1 ( X H Z ) X − 1 X H X − 1 Z H X − 1 Z X^{-1}Y X^{-1}(XHZ)…

LeetCode213 打家劫舍 II 动态规划法

题目地址 https://leetcode.cn/problems/house-robber-ii/ 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋&#xff0c;每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 &#xff0c;这意味着第一个房屋和最后一个房屋是紧挨着的。同时&#xff0c;相邻的房屋装…

【Hive实战】探索Hive 2.X以及更早版本的MetaStore

探索Hive 2.X以及更早版本的MetaStore 文章目录 探索Hive 2.X以及更早版本的MetaStore概述配置元数据服务和元数据存储库基础配置参数其他配置参数默认配置配置元服务数据库使用内嵌模式的Derby库使用远程数据存储库 配置元数据服务本地/内嵌服务配置远程服务配置 元数据服务配…

【KingSCADA】什么是精灵图以及如何创建精灵图

大家好&#xff0c;我是雷工&#xff01; 本篇学习精灵图的制作&#xff0c;以下为学习内容及相关笔记。 一、什么是精灵图 精灵图是一种在外观上类似组合图&#xff0c;但内部嵌入了比较丰富的动画链接与逻辑控制&#xff0c;工程开发人员只要将其从精灵图库中调出来放置在开…

MySQL基础练习——创建数据库、数据表,并进行修改

目录 题目&#xff1a; 创建库和表&#xff1a; 创建库&#xff1a; 创建表&#xff1a; 将 c_contact 字段插入到 c_birth 字段后面&#xff1a; 将 c_name 字段数据类型改为VARCHAR(70)&#xff1a; 将 c_contact 字段改名为 c_phone&#xff1a; 将表名修改为 customer…

AD9208调试经验分享

背景概述 FMC137 是一款基于 VITA57.4 标准规范的 JESD204B 接口FMC 子 卡 模 块 &#xff0c; 该 模 块 可 以 实 现 4 路 14-bit 、 2GSPS/2.6GSPS/3GSPSADC 采集功能。该板卡 ADC 器件采用 ADI 公司的 AD9208 芯片&#xff0c;&#xff0c;与 ADI 公司的 AD9689 可以实现 …

量子力学 学习

对于同一个竖直向上量子比特&#xff0c;不对他进行任何的干扰&#xff0c;进行第一次水平测试实验会随机得到一个一或者负一&#xff0c;之后再进行多少次水平测试实验都与第一次的试验结果是相同的。 我们换用其他的竖直向上量子比特&#xff0c;或者对原来的量子比特进行干扰…

Matlab绘图中的一些技能

目录 1、matlab坐标轴设置多种字体(复合字体) 2、matlab图片中title生成的标题转移至图像下端 3、指定对应格式和期望dpi的图像进行保存、以及不留白保存 4、设置字体字号&#xff08;x、y轴&#xff0c;标题。全局字体等&#xff09; 5、设置刻度值信息&#xff0c;只有左…

引领文旅新体验!实时云渲染助力打造“永不落幕”的湾区文采会元宇宙

2022年11月25日至27日&#xff0c;2022年粤港澳大湾区公共文化和旅游产品&#xff08;东莞&#xff09;采购会&#xff08;简称“湾区文采会”&#xff09;在广东省东莞市文化馆举行。 文采会期间&#xff0c;文采会元宇宙线上虚拟展厅全新亮相&#xff0c;这艘承载着科技与文化…

5款十分小众的软件,知道的人不多但却很好用

今天推荐5款十分小众的软件&#xff0c;知道的人不多&#xff0c;但是每个都是非常非常好用的&#xff0c;有兴趣的小伙伴可以自行搜索下载。 1.视频直播录制——OBS Studio OBS Studio可以让你轻松地录制和直播你的屏幕、摄像头、游戏等内容。你可以使用OBS Studio来创建多种…

Mysql设置表只存储一段时间的数据

使用MySQL的事件调度器&#xff08;Event Scheduler&#xff09;来定期删除表中的数据。 假设你要删除的表是mytable&#xff0c;并且表中有一个名为created_at的日期时间类型的列&#xff0c;存储了每条记录的创建时间。你可以通过以下步骤设置表只存储30天的数据&#xff1a…

机器学习 协同过滤算法

协同过滤算法 协同过滤算法是根据已有的数据来推测出未知的数据&#xff0c;从海量的数据中找到相似度达到指定范围的数据&#xff0c;而这些数据成为你的邻居&#xff0c;系统将会为你推荐心仪的物品。 余弦相似法 通过计算两个向量的夹角余弦值来评估它们的相似度 修正余弦…

《站在巨人的肩膀上学习Java》

Java从诞生距今已经有28年了&#xff0c;在这段时间里&#xff0c;随着Java版本的不断迭代&#xff0c;Java新特性的不断出现&#xff0c;使得Java被使用的越来越广泛。在工程界Java语言一直是大家最喜欢的语言之一&#xff0c;Java一直排行在编程语言热门程度的前3名。 可想而…

从0搭建Vue3组件库(六):前端流程化控制工具gulp的使用

随着前端诸如webpack&#xff0c;rollup&#xff0c;vite的发展&#xff0c;gulp感觉似乎好像被取代了。其实并没有&#xff0c;只不过它从台前退居到了幕后。我们仍然可以在很多项目中看到它的身影&#xff0c;比如elementplus、vant等。现在gulp更多的是做流程化的控制。 比如…

delta.io 参数 spark.databricks.delta.replaceWhere.constraintCheck.enabled

总结 默认值true 你写入的df分区字段必须全部符合覆盖条件 .option("replaceWhere", "c2 == 2") false: df1 overwrite tb1: df1中每个分区的处理逻辑: - tb1中存在(且谓词中匹配)的分区,则覆盖 - tb1中存在(谓词中不匹配)的分区,则append - tb1中不存…

今天试了试chatgpt

今天试了试chatgpt&#xff0c;真是服了 arcade&#xff1f; Arcade是一个Python游戏开发库&#xff0c;它提供了一系列的工具和函数&#xff0c;可以帮助开发者快速地创建2D游戏。以下是Arcade的一些特点&#xff1a; 简单易用&#xff1a;Arcade提供了简单易用的API&#x…

Android分屏流程分析

本文基于Android 11。 SystemUI模块中的Divider管理着所有关于分屏的对象&#xff1a; DividerView&#xff08;分屏分割线&#xff0c;分屏显示界面&#xff09;SplitScreenTaskOrganizer&#xff08;分屏Task组织者&#xff0c;分屏逻辑&#xff09; 这里重点关注分屏逻辑…

Qt如何生成dump文件和pdb文件并进行调试定位

在main文件中增加下面代码用于可生成dump文件 #include "widget.h" #include <QApplication> #include <QDir> #include <QDateTime> #ifdef Q_OS_WIN#include <windows.h>#include <dbghelp.h> #endifstatic LONG WINAPI exceptionC…

简单介绍一下什么是“工作内存”和“主内存”(JMM中的概念)

在学习Java多线程编程里&#xff0c; volatile 关键字保证内存可见性的要点时&#xff0c;看到网上有些资料是这么说的&#xff1a;线程修改一个变量&#xff0c;会把这个变量先从主内存读取到工作内存&#xff1b;然后修改工作内存中的值&#xff0c;最后再写回到主内存。 对…

Spring 循环依赖处理之三级缓存设计

一、思考 1、Spring是如何解决循环依赖问题的? 2、为什么要使用三级缓存?二级缓存能否解决问题? 3、提前暴露对象暴露的是什么? 4、主要源码 二、循环依赖 1、介绍 如上图&#xff0c;创建A之前需要先创建B,创建B之前需要先创建A,造成循环依赖。 由于A没创建完成&am…