news 2026/4/21 4:17:45

6.新特性-泛型

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
6.新特性-泛型

为什么会引入泛型

泛型允许类、接口和方法在定义时使用一个或多个类型参数,使得它们可以在编译时具有更强的类型检查,并且能够避免类型转换错误。
privatestaticintadd(inta,intb){System.out.println(a+"+"+b+"="+(a+b));returna+b;}privatestaticfloatadd(floata,floatb){System.out.println(a+"+"+b+"="+(a+b));returna+b;}privatestaticdoubleadd(doublea,doubleb){System.out.println(a+"+"+b+"="+(a+b));returna+b;}

如果没有泛型,要实现不同类型的加法,每种类型都需要重载一个add方法;通过泛型,我们可以复用为一个方法:

privatestatic<TextendsNumber>doubleadd(Ta,Tb){System.out.println(a+"+"+b+"="+(a.doubleValue()+b.doubleValue()));returna.doubleValue()+b.doubleValue();}

泛型中的类型在使用时指定,不需要强制类型转换(类型安全,编译器会检查类型)

Listlist=newArrayList();list.add("xxString");list.add(100d);list.add(newPerson());

在使用上述list中,list中的元素都是Object类型(无法约束其中的类型),所以在取出集合元素时需要人为的强制类型转化到具体的目标类型,且很容易出现java.lang.ClassCastException异常。
引入泛型,它将提供类型的约束,提供编译前的检查:

List<String>list=newArrayList<String>();//list中只能放String, 不能放其它类型的元素

泛型类

//T仅仅是标记,可以任意定义publicclassPoint<T>{//var是T类型,由外部传入privateTvar;publicvoidsetVar(Tvar){this.var=var;}publicTgetVar(){returnvar;}publicstaticvoidmain(String[]args){Point<String>p=newPoint<String>();p.setVar("jack");System.out.println(p.getVar().length());}}

多元泛型

//此处指定了两个泛型类型publicclassNotepad<K,V>{privateKkey;//此变量的类型由外部决定privateVvalue;//此变量的类型由外部决定privatevoidsetKey(Kkey){this.key=key;}privatevoidsetValue(Vvalue){this.value=value;}privateKgetKey(){returnthis.key;}privateVgetValue(){returnthis.value;}publicstaticvoidmain(String[]args){Notepad<String,Integer>t=newNotepad<>();t.setKey("jack");//设置第一个内容t.setValue(33);//设置第二个内容System.out.println("name: "+t.getKey());System.out.println("age: "+t.getValue());}}

泛型接口

//在接口上定义泛型publicinterfaceInfo<T>{// 定义抽象方法,抽象方法的返回值就是泛型类型publicTgetVar();}
// 定义泛型接口的子类classInfoImpl<T>implementsInfo<T>{//定义属性privateTvar;//通过构造方法设置属性内容publicInfoImpl(Tvar){this.setVar(var);}publicvoidsetVar(Tvar){this.var=var;}@OverridepublicTgetVar(){returnthis.var;}publicstaticvoidmain(String[]args){Info<String>info=newInfoImpl<>("JACK");System.out.println("info: "+info.getVar());}}

泛型方法

泛型方法,是在调用方法的时候指明泛型的具体类型。



说明一下,定义泛型方法时,必须在返回值前边加一个,来声明这是一个泛型方法,持有一个泛型T,然后才可以用泛型T作为方法的返回值。

Class的作用就是指明泛型的具体类型,而Class类型的变量c,可以用来创建泛型类的对象。

为什么要用变量c来创建对象呢?既然是泛型方法,就代表着我们不知道具体的类型是什么,也不知道构造方法如何,因此没有办法去new一个对象,但可以利用变量c的newInstance方法去创建对象,也就是利用反射创建对象。

泛型方法要求的参数是Class类型,而Class.forName()方法的返回值也是Class,因此可以用Class.forName()作为参数。其中,forName()方法中的参数是何种类型,返回的Class就是何种类型。在本例中,forName()方法中传入的是User类的完整路径,因此返回的是Class类型的对象,因此调用泛型方法时,变量c的类型就是Class,因此泛型方法中的泛型T就被指明为User,因此变量obj的类型为User。

为什么要使用泛型方法呢?
因为泛型类要在实例化的时候就指明类型,如果想换一种类型,不得不重新new一次,可能不够灵活;而泛型方法可以在调用的时候指明类型,更加灵活。

泛型的上下限

classA{}classBextendsA{}// 如下两个方法不会报错publicstaticvoidfunA(Aa){// ...}publicstaticvoidfunB(Bb){funA(b);// ...}// 如下funD方法会报错publicstaticvoidfunC(List<A>listA){// ...}publicstaticvoidfunD(List<B>listB){funC(listB);// Unresolved compilation problem: The method doPrint(List<A>) in the type test is not applicable for the arguments (List<B>)// ...}

解决方案
1.为了解决泛型中隐含的转换问题,Java泛型加入了类型参数的上下边界机制。
2.<? extends A>表示该类型参数可以是A(上边界)或者A的子类类型。
3.编译时擦除到类型A,即用A类型代替类型参数。
4.这种方法可以解决开始遇到的问题,编译器知道类型参数的范围,如果传入的实例类型B是在这个范围内的话允许转换,这时只要一次类型转换就可以了,运行时会把对象当做A的实例看待。

publicstaticvoidfunC(List<?extendsA>listA){// ...}publicstaticvoidfunD(List<B>listB){funC(listB);// OK// ...}

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

上限

publicclassInfo<TextendsNumber>{privateTvar;privateTgetVar(){returnthis.var;}privatevoidsetVar(Tvar){this.var=var;}publicStringtoString(){returnthis.var.toString();}publicstaticvoidmain(String[]args){Info<Integer>ii=newInfo<Integer>();ii.setVar(33);System.out.println(ii.getVar());}}

下限

packagecom.heima.base.fanxing;publicclassInfo<T>{privateTvar;publicvoidsetVar(Tvar){this.var=var;}publicTgetVar(){returnthis.var;}publicStringtoString(){returnthis.var.toString();}publicstaticvoidmain(String[]args){//声明String的泛型对象Info<String>i1=newInfo<String>();//声明Object的泛型对象Info<Object>i2=newInfo<Object>();i1.setVar("jack");i2.setVar(newObject());fun(i1);fun(i2);}//只能接收String或Object类型的泛型,String类的父类只有Object类publicstaticvoidfun(Info<?superString>temp){System.out.println(temp+",");}}

小结

<?>无限制通配符<?extendsE>extends关键字声明了类型的上界,表示参数化的类型可能是所指定的类型,或者是此类型的子类<?superE>super关键字声明了类型的下界,表示参数化的类型可能是指定的类型,或者是此类型的父类// 使用原则《Effictive Java》// 为了获得最大限度的灵活性,要在表示 生产者或者消费者 的输入参数上使用通配符,使用的规则就是:生产者有上限、消费者有下限1.如果参数化类型表示一个T的生产者,使用<?extendsT>;2.如果它表示一个T的消费者,就使用<?superT>3.如果既是生产又是消费,那使用通配符就没什么意义了,因为你需要的是精确的参数类型。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/21 4:17:40

手把手教你用AD9363开发板接收GPS信号(附FPGA配置与ILA抓波实战)

基于AD9363开发板的GPS信号接收实战指南 当我在实验室第一次用AD9363开发板捕获到GPS信号时&#xff0c;那种成就感至今难忘。这款集成了射频前端的芯片确实为嵌入式开发者打开了无线通信的大门。本文将分享从硬件配置到信号分析的全流程实战经验&#xff0c;特别适合已经拥有类…

作者头像 李华
网站建设 2026/4/21 4:16:16

SpringBoot+Vue美妆电商系统源码+论文

代码可以查看文章末尾⬇️联系方式获取&#xff0c;记得注明来意哦~&#x1f339; 分享万套开题报告任务书答辩PPT模板 作者完整代码目录供你选择&#xff1a; 《SpringBoot网站项目》1800套 《SSM网站项目》1500套 《小程序项目》1600套 《APP项目》1500套 《Python网站项目》…

作者头像 李华
网站建设 2026/4/21 4:14:14

告别裸奔通信:手把手教你用Petalinux 2020.1为Zynq7000配置OpenAMP异构框架

异构通信实战&#xff1a;基于Petalinux 2020.1的Zynq7000 OpenAMP全流程配置指南 当一块Zynq7000开发板放在你面前&#xff0c;如何让它的双核Cortex-A9与实时协处理器R5实现高效对话&#xff1f;这就像让两位使用不同语言的专家协作完成项目——需要建立精确的通信协议。本文…

作者头像 李华
网站建设 2026/4/21 4:12:52

repo 切换 分支

Repo/Git 常用命令使用_repo查看提交记录-CSDN博客 repo forall -c git checkout release-v2.14 repo forall -c git checkout release-v2.14 #查看是否切换成功. repo forall -c git branch --show-currentrepo init -u ssh://xxxx:418/Projects/xxx/manifest -m android_…

作者头像 李华
网站建设 2026/4/21 4:10:17

MicroBurst REST API攻击实战:绕过传统PowerShell模块限制

MicroBurst REST API攻击实战&#xff1a;绕过传统PowerShell模块限制 【免费下载链接】MicroBurst A collection of scripts for assessing Microsoft Azure security 项目地址: https://gitcode.com/gh_mirrors/mi/MicroBurst 什么是MicroBurst REST API攻击&#xff…

作者头像 李华
网站建设 2026/4/21 4:07:17

别再死记硬背了!用MySQL的`rand(0)`和`group by`亲手复现一次SQL报错注入

从零复现MySQL报错注入&#xff1a;用rand(0)和group by破解SQL防御机制 当你第一次听说SQL注入时&#xff0c;脑海中浮现的可能是黑客在电影里快速敲击键盘的画面。但现实中的SQL注入更像是一场精心设计的数学魔术——而今天&#xff0c;我们要揭秘的就是其中最精妙的"报…

作者头像 李华