黑马程序员_java基础复习之七泛型

本文深入讲解Java泛型的基础知识,包括泛型的引入背景、泛型的术语解释、泛型的使用方法及其限制条件等。文章还介绍了如何自定义泛型类和方法,并通过实例演示了类型推断和反射获取泛型参数化类型的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

---------android培训、java培训期待与您交流! ---------

一,泛型基础知识


jdk1.5之后出现的新特性


泛型是提供给javac编译器使用的,它可以限定集合中输入的类型,让编译器挡住源程序中的非法输入,编译器编译带类型的说明的集合时会去除掉"类型"信息,是程序运行效率不受影响。对应参数化的泛型类型,getClass()方法的返回值和原始类型完全一样。由于编译生成的字节码会去掉泛型的类型信息,只要能跳过编译器,就可以往某个泛型集合中加入其他类型的数据。例如用反射得到的集合,再调用add方法即可。


泛型是编译器看的,所以通过反射可以避过编译器,继续把不同的类型的值放进已经被泛型限定了的集合里面。


体验泛型


Jdk 1.5以前的集合类中存在什么问题
ArrayList collection = new ArrayList();
collection.add(1);
collection.add(1L);
collection.add("abc");
int i = (Integer) collection.get(1);//编译要强制类型转换且运行时出错!
Jdk 1.5的集合类希望你在定义集合时,明确表示你要向集合中装哪种类型的数据,无法加入指定类型以外的数据
ArrayList<Integer> collection2 = new ArrayList<Integer>();
collection2.add(1);
/*collection2.add(1L);
collection2.add(“abc”);*///这两行代码编译时就报告了语法错误
int i2 = collection2.get(0);//不需要再进行类型转换


泛型中的术语:
ArrayList<E>类定义和ArrayList<Integer>类引用中涉及如下术语:
整个称为ArrayList<E>泛型类型
ArrayList<E>中的E称为类型变量或类型参数
整个ArrayList<Integer>称为参数化的类型
ArrayList<Integer>中的Integer称为类型参数的实例或实际类型参数
ArrayList<Integer>中的<>念着typeof
ArrayList称为原始类型

 


参数化类型与原始类型的兼容性:
参数化类型可以引用一个原始类型的对象,编译报告警告,例如, Collection<String> c = new Vector();
原始类型可以引用一个参数化类型的对象,编译报告警告,例如, Collection c = new Vector<String>();//原来的方法接受一个集合参数,新的类型也要能传进去
参数化类型不考虑类型参数的继承关系:
Vector<String> v = new Vector<Object>(); //错误!///不写<Object>没错,写了就是明知故犯
Vector<Object> v = new Vector<String>(); //也错误!
编译器不允许创建泛型变量的数组。即在创建数组实例时,数组的元素不能使用参数化的类型,例如,下面语句有错误:
Vector<Integer> vectorList[] = new Vector<Integer>[10];

 


为什么在创建数组实例时,数组的元素不能使用参数化的类型?
编译器是严格按照步骤走的。它检测每一行代码的信息是否有误,但是不会用执行时候的答案来检测代码的正确性。

 


泛型限定:


泛型的通配符(?)应用:?表示任意类型。比如当要定义一个任意参数化类型的集合(Collection<?> col),就用得到了通配符。


使用?通配符可以引用其他各种参数化的类型,?通配符定义的变量主要用作引用,可以调用与参数化无关的方法,不能调用与参数化有关的方法。比如集合中的add()方法,使用了?的集合就不能在调用这个方法。


public void printCollection(Collection<?> col){}


也就是说Collection<?> col = new ArrayList<String>();编译器让这个表达式被编译通过。

问题:
定义一个方法,该方法用于打印出任意参数化类型的集合中的所有数据,该方法如何定义呢?
错误方式:
public static void printCollection(Collection<Object> cols) {
for(Object obj:cols) {
System.out.println(obj);
}
/* cols.add("string");//没错
cols = new HashSet<Date>();//会报告错误!*/
}
正确方式:
public static void printCollection(Collection<?> cols) {
for(Object obj:cols) {
System.out.println(obj);
}
//cols.add("string");//错误,因为它不知自己未来匹配就一定是String
cols.size();//没错,此方法与类型参数没有关系
cols = new HashSet<Date>();
}
总结:
使用?通配符可以引用其他各种参数化的类型,?通配符定义的变量主要用作引用,可以调用与参数化无关的方法,不能调用与参数化有关的方法。


泛型中的?通配符的扩展

?extends E:可以接收E类型或者E的子类。这是上限限定
?super E:可以接收E类型或者E的父类。这是下限限定


限定通配符的上边界:
正确:Vector<? extends Number> x = new Vector<Integer>();
错误:Vector<? extends Number> x = new Vector<String>();


限定通配符的下边界:
正确:Vector<? super Integer> x = new Vector<Number>();
错误:Vector<? super Integer> x = new Vector<Byte>();


提示:
限定通配符总是包括自己。
?只能用作引用,不能用它去给其他变量赋值
Vector<? extends Number> y = new Vector<Integer>();
Vector<Number> x = y;
上面的代码错误,原理与Vector<Object > x11 = new Vector<String>();相似,
只能通过强制类型转换方式来赋值。

 


自定义泛型


KV都可以代替任意类型的值,但是在java中范型的实际类型必须是引用类型


<K,V> void get(K k,V v)
{
}


Java中的范型不能像C++那么灵活


<T>  T  add(T a,T b)
{
   //return  a+b   ;//很多人以为java也想C++一样可以这样 ,但是不可以 。    
return  null;
}


这个返回的null也是有类型限制的,比如上面的ab分别是Integer和String那么就会取他们共同的基类Object做为返回值类型,其他的同理
例子:
Number x1=add(3.5,3);//Integer和float他们共同的基类为Number
Object x2=add("abc",3);//Integer和String他们共同的基类为Object


实现任意类型的数组的成员值的交换,注意在自定义范型中范型的实际类型只能是引用数据类型不能是基本数据类型


public  static <T> void  swap(T[]a,int x,int y)
{
  T  tem  =a[x]  ;
  a[x]=a[y]  ;
  a[y]=tem ;
}
上面这个方法如果  swap(new Integer[]{1,2,3,4,5},1,2);       //这样就会自动交换下标12的值
但是这样调用就错了   swao(new int[]{1,2,3,5,6},2,3) ;  //所以说Java的范型的实际类型 只能是引用数据类型


泛型练习
1,编写一个泛型方法,自动将Object类型的对象转换成其他类型


public static  <T> T autoConvert(Object obj)  //因为返回值是 T标识任意类型 所哟可以 将返回结果赋值给任意类型对象
{
  return (T)obj;
}


Object  obj=="abc";


String str=autoConvert(obj);


可以完成自动转换,因为范型T代表任意类型,因此他可以赋值给String类型的对象


2,定义一个方法,可以将任意类型的数组中的所有元素填充为相应类型的某个对象。


public  static <T> void  fillArray(T[] a,T b)  //将任意一个对象填充到任意类型的数组
{
  for(int i =0;i<a.length;i++)
  {
   a[i] =b ;
  }
}


3,采用自定泛型方法的方式打印出任意参数化类型的集合中的所有内容。


public static  <T> void showCollection(Collection<T> col,T  obj)  //利用范型来输出任意类型集合

  col.add(obj) ;
  for(T a:col)
  {
   System.out.println(a);
  }
}


public static void showCollection(Collection<?> col)  //利用范型来输出任意类型集合

  for(Object obj:col)
  {
   System.out.println(obj);
  }
}


4,定义一个方法,把任意参数类型的集合中的数据安全地复制到相应类型的数组中。


public static <T> void copy1(Collection<T> dest,T[] src){

}


5,定义一个方法,把任意参数类型的一个数组中的数据安全地复制到相应类型的另一个数组中。


public static <T> void copy2(T[] dest,T[] src){

}


如果一个类中多个方法都需要范型那么就是用类级别的范型。例如

class  A<E> 

     public void  add(E obj){}
     public  E  get(){} 
     private E data; 
}


这样声明范型和在函数前面声明其实是一样的只不过是在类的级别上作用于整个类而已


要注意范型只是给编译器看的。


也就是说Vector<Integer>Vector<String>他们用到的都是同一份字节码,字节码只有class文件加载到内存中的时候才有

所以在一个类中下面2个方法不能同时存在
void show(Vector<Integer>) {}
void show(Vector<String>){}

类型参数的类型推断


编译器判断范型方法的实际类型参数的过程称为类型推断,类型推断是相对于知觉推断的,其实现方法是一种非常复杂的过程。
根据调用泛型方法时实际传递的参数类型或返回值的类型来推断,具体规则如下:
当某个类型变量只在整个参数列表中的所有参数和返回值中的一处被应用了,那么根据调用方法时该处的实际应用类型来确定,这很容易凭着感觉推断出来,即直接根据调用方法时传递的参数类型或返回值来决定泛型参数的类型,例如:
swap(new String[3],3,4)   ?    static <E> void swap(E[] a, int i, int j)
当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型都对应同一种类型来确定,这很容易凭着感觉推断出来,例如:
add(3,5)   ? static <T> T add(T a, T b)
当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型对应到了不同的类型,且没有使用返回值,这时候取多个参数中的最大交集类型,例如,下面语句实际对应的类型就是Number了,编译没问题,只是运行时出问题:
fill(new Integer[3],3.5f)   ? static <T> void fill(T[] a, T v)
当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型对应到了不同的类型, 并且使用返回值,这时候优先考虑返回值的类型,例如,下面语句实际对应的类型就是Integer了,编译将报告错误,将变量x的类型改为float,对比eclipse报告的错误提示,接着再将变量x类型改为Number,则没有了错误:
int x =(3,3.5f)   ? static <T> T add(T a, T b)
参数类型的类型推断具有传递性,下面第一种情况推断实际参数类型为Object,编译没有问题,而第二种情况则根据参数化的Vector类实例将类型变量直接确定为String类型,编译将出现问题:
copy(new Integer[5],new String[5]) ? static <T> void copy(T[] a,T[]  b);
copy(new Vector<String>(), new Integer[5]) ? static <T> void copy(Collection<T> a , T[] b);


通过反射获得泛型的参数化类型


jdk1.5开始,一个方法可以通过反射获取到他的参数的参数化类型比如:put(Vector<Date> v),通过反射可以获取Date的类型


1.Method里面的方法:


Type[] getGenericParameterTypes()


按照声明顺序返回描述了此 Method 对象所表示的方法的形参类型的 Type 对象的数组。


Type getGenericReturnType()


返回表示由此 Method 对象所表示方法的正式返回类型的 Type 对象。


2.Type是接口,是ParameterizedType的父类


3.ParameterizedType 参数化类型 它放的是泛型的参数化类型实例


Type[] getActualTypeArguments()


返回表示参数的类的的类型参数的Type对象数组。


Type getOwnerType()


返回 Type 对象,返回此类的顶层类的类型。比如O<T>.I<S>,则返回 O<T> 的表示形式


Type getRawType()


返回 Type 对象,表示声明此类型的类或接口。


代码示例


Method applyMethod = GenericTest.class.getMethod("applyVector", Vector.class);
Type[] types = applyMethod.getGenericParameterTypes();
ParameterizedType pType = (ParameterizedType)types[0];
System.out.println(pType.getRawType());
System.out.println(pType.getActualTypeArguments()[0]);


public static void applyVector(Vector<Date> v1){

}

---------android培训、java培训期待与您交流! ---------


内容概要:该论文探讨了一种基于粒子群优化(PSO)的STAR-RIS辅助NOMA无线通信网络优化方法。STAR-RIS作为一种新可重构智能表面,能同时反射和传输信号,与传统仅能反射的RIS不同。结合NOMA技术,STAR-RIS可以提升覆盖范围、用户容量和频谱效率。针对STAR-RIS元素众多导致获取完整信道状态信息(CSI)开销大的问题,作者提出一种在不依赖完整CSI的情况下,联合优化功率分配、基站波束成形以及STAR-RIS的传输和反射波束成形向量的方法,以最大化总可实现速率并确保每个用户的最低速率要求。仿真结果显示,该方案优于STAR-RIS辅助的OMA系统。 适合人群:具备一定无线通信理论基础、对智能反射面技术和非正交多址接入技术感兴趣的科研人员和工程师。 使用场景及目标:①适用于希望深入了解STAR-RIS与NOMA结合的研究者;②为解决无线通信中频谱资源紧张、提高系统性能提供新的思路和技术手段;③帮助理解PSO算法在无线通信优化问题中的应用。 其他说明:文中提供了详细的Python代码实现,涵盖系统参数设置、信道建模、速率计算、目标函数定义、约束条件设定、主优化函数设计及结果可视化等环节,便于读者理解和复现实验结果。此外,文章还对比了PSO与其他优化算法(如DDPG)的区别,强调了PSO在不需要显式CSI估计方面的优势。
内容概要:本文档详尽介绍了人机交互与网页开发课程作业的复现过程,涵盖内容开发、UI原设计、网站开发、多保真度原设计、创意工具和技术应用、网站必备功能实现、学术诚信与提交指南、评分标准等方面。具体包括用户需求分析、低保真和高保真原设计、前端和后端开发示例、数据库设计、可用性测试、故事板设计、响应式设计、3D交互元素创建、备份工具、管理面板、原创性检查系统、标题页生成器、评分标准检查表、学术写作质量分析器、评分标准映射系统、项目质量保证检查表、低分项目诊断与改进系统、评分标准转换工具以及学术诚信教育模块。每个部分都提供了详细的代码实现和中文解释,确保项目符合课程要求并达到较高的评分标准。 适合人群:适用于正在学习人机交互与网页开发课程的学生,尤其是需要完成类似课程作业的人群;同时也适合希望深入了解Web开发全流程的专业人士。 使用场景及目标:①帮助学生理解并完成课程作业,包括从需求分析到最终提交的全过程;②为开发者提供实际项目开发中的技术参考,如前后端开发、数据库设计、响应式设计等;③确保项目的学术诚信,提供原创性检查和学术写作质量分析工具;④帮助学生理解评分标准,提供详细的评分映射和改进建议;⑤提供学术诚信教育,确保学生了解并遵守学术规范。 其他说明:本文档不仅提供了技术实现的代码示例,还涵盖了项目管理和学术诚信方面的内容,确保学生能够在技术、管理和学术三个方面都能得到全面提升。此外,文档还提供了多种工具和系统,帮助学生更好地准备和优化他们的课程作业,从而获得更高的评分。
标题SpringBoot智能垃圾分类系统研究AI更换标题第1章引言介绍智能垃圾分类系统的研究背景、意义、现状以及论文的研究方法和创新点。1.1研究背景与意义阐述智能垃圾分类系统的重要性及其在现实中的应用价值。1.2国内外研究现状概述国内外在智能垃圾分类系统方面的研究进展及成果。1.3研究方法与创新点介绍本论文采用的研究方法以及创新点。第2章相关理论介绍SpringBoot框架和智能垃圾分类的相关理论和技术。2.1SpringBoot框架概述阐述SpringBoot框架的基本概念、特点和优势。2.2垃圾分类技术介绍传统的垃圾分类方法和智能垃圾分类技术的原理及应用。2.3机器学习算法在垃圾分类中的应用讨论机器学习算法在智能垃圾分类系统中的关键作用。第3章SpringBoot智能垃圾分类系统设计详细介绍基于SpringBoot的智能垃圾分类系统的设计方案和实现过程。3.1系统架构设计给出系统的整体架构,包括前端、后端和数据库等组件。3.2智能分类模块设计阐述智能分类模块的具体设计,包括图像识别、传感器数据采集等功能。3.3系统安全性设计讨论系统在安全性方面的设计和实现,如用户认证、数据加密等。第4章系统实现与测试介绍SpringBoot智能垃圾分类系统的具体实现过程以及测试方法和结果。4.1系统实现详细阐述系统的实现过程,包括关键代码和技术难点。4.2系统测试方法与步骤给出系统测试的具体方法和步骤,包括单元测试、集成测试和系统测试等。4.3测试结果与分析对测试结果进行详细分析,验证系统的功能和性能是否达到预期目标。第5章结论与展望总结SpringBoot智能垃圾分类系统的研究成果,并展望未来的研究方向和应用前景。5.1研究结论概括本论文的主要研究结论和创新点,以及系统在实际应用中的表现。5.2展望分析当前研究的局限性,提出未来可能的研究方向和改进措施。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值