简述泛型的基本使用和作用

article/2025/6/20 16:30:34

前言

在上一篇文章中,给大家讲解了泛型的概念、作用、使用场景,以及泛型集合、泛型接口和泛型类的用法,但受限于篇幅,并没有把泛型的内容讲解完毕。所以今天我们会继续学习泛型方法、泛型擦除,以及通配符等的内容,希望大家继续做好学习的准备哦。


全文大约【4600】 字,不说废话,只讲可以让你学到技术、明白原理的纯干货!本文带有丰富的案例及配图视频,让你更好地理解和运用文中的技术概念,并可以给你带来具有足够启迪的思考…

一. 泛型方法

1. 简介

我们可以在定义接口和类时使用泛型,这样该接口和类中的所有方法及成员变量等处,也都可以使用该泛型。但其实泛型可以应用在整个类上,也可以只应用在类中的某个方法上。也就是说,方法所在的类可以是泛型类,也可以不是泛型类。方法中是否带有泛型,与其所在的类有没有泛型没有关系。

泛型方法是在调用方法时才确定类型的方法,泛型可以使得该方法独立于类而产生变化。另外,static静态方法无法访问泛型类的类型参数,因此,如果想让一个static方法具有泛型能力,就必须使该静态方法成为泛型方法。

2. 语法

我们在定义泛型方法时,需要在方法名的前面添加类型参数。定义泛型方法的语法格式如下:

[访问权限修饰符] [static] [final] <类型参数列表> 返回值类型 方法名([形式参数列表])

例如:

public static <T> List showInfo(Class<T> clazz,int userId){}

一般情况下,我们编写泛型方法时,必须在该方法的名称前声明要使用的泛型,并且可以同时声明多个泛型,中间也是用逗号分割。接下来就定义一个泛型方法,给大家具体介绍一下泛型方法的创建和使用。

3. 代码案例

这里我们定义一个泛型方法,用于对数组排序后再遍历元素输出,代码如下:

import java.util.Arrays;public class Demo04 {//定义了一个静态的泛型方法,遍历数组中的每个元素public static <T> void printArray(T[] arr) {//先对数组进行排序Arrays.sort(arr);//再遍历数组元素for (T t : arr) {System.out.print(t + " ");}System.out.println();}public static void main(String[] args) {Integer[] nums= {100,39,8,200,65};//调用泛型方法printArray(nums);}
}

在上面的代码中,printArray()就是一个泛型方法,该方法中使用了类型参数T。并且我们在方法的参数中,使用类型参数T定义了一个泛型数组T[],接着对该数组进行排序和遍历。这样以后无论我们传入任何类型的数组,都可以在不进行类型转换的前提下,轻松实现排序等功能了,这样我们之前提的需求也就很容易实现了。

二. 通配符

除了以上这些用法之外,泛型中还有一个很重要的通配符功能,接下来我们就来看看它是怎么回事。

1. 简介

泛型中的通配符其实也是一种特殊的泛型类型,也称为通配符类型参数。利用通配符类型参数,可以让我们编写出更通用的代码,甚至可以在不知道实际类型的情况下使用它们。我们一般是使用 ? 来代替具体的类型参数,例如 List<?> 在逻辑上可以等同于 List、List 等所有 List<具体类型实参> 的类。

对此,有的小伙伴可能会很好奇,我们为什么需要通配符呢?其实之所以会出现通配符,主要是在开发时,有时候我们需要一个泛型类型,但我们却不知道该使用哪个具体的类型。在这种情况下,我们就可以使用通配符类型参数,让代码更加地通用。比如,我们想编写一个可以接受任何类型的集合,并返回其中最大的元素时,此时我们可能并不确定到底该传入哪个具体的集合,那使用通配符就会更好一些。

2. 通配符的形式

泛型通配符在具体使用时,有如下三种实现形式:

  • 未限定通配符(?)?表示未知类型的通配符
  • 上限通配符(? extends T)?表示类型上限的通配符,T是一个类或接口
  • 下限通配符(? super T)?表示类型下限的通配符,T是一个类或接口

接下来我们针对以上这三种形式,分别通过几个案例来给大家讲解其用法。

3. 未限定通配符(?)

未限定通配符(?)是一种表示未知类型的通配符,它可以在需要一个类型参数的情况下使用。但由于没有限制,因此它只能用于简单的情况,例如集合中的迭代器或者返回类型是泛型的方法等。下面是一个简单的例子:

import java.util.ArrayList;
import java.util.List;public class Demo05 {public static void main(String[] args) {List<String> names = new ArrayList<String>();List<Integer> ages = new ArrayList<Integer>();List<Number> numbers = new ArrayList<Number>();names.add("一一哥");names.add("秦始皇");ages.add(28);ages.add(50);ages.add(28);numbers.add(100);numbers.add(800);printElement(names);printElement(ages);printElement(numbers);}//未限定通配符的使用public static void printElement(List<?> data) {for(int i=0;i<data.size();i++) {//data.getClass().getSimpleName():用于获取某个类的类名System.out.println(data.getClass().getSimpleName()+"--data: " + data.get(i));}}}

在这个例子中,printElement()方法就接受了一个未知类型的List集合,所以names,ages,numbers都可以作为这个方法的实参,这就是未限定通配符的作用。

4. PECS原则

PECS是Producer Extends Consumer Super的缩写,这是关于Java泛型的一种设计原则。该原则表示,如果我们需要返回T,它是生产者(Producer),要使用extends通配符;如果需要写入T,它就是消费者(Consumer),要使用super通配符。该原则可以指导我们在使用泛型时,该如何定义类型参数的上限和下限。

当我们使用泛型时,可能需要定义类型参数的上限和下限。

例如,我们想要编写一个方法来处理一些集合类型,但我们不知道这些集合中到底有什么类型的元素,此时我们就可以定义一个类型参数来处理所有的集合类型。一般我们可以利用extends来设置泛型上限,利用super来设置泛型下限。接下来会在下面的第5和第6小结中,给大家讲解泛型的上限和下限具体该如何实现,请大家继续往下学习。

5. 上限通配符(? extends T)

上限通配符(?extends T)是一种表示类型上限的通配符,其中T是一个类或接口,泛型类的类型必须实现或继承 T这个接口或类。它指定了可以使用的类型上限,主要是用于限制输入的参数类型。

import java.util.ArrayList;
import java.util.List;/*** @author 一一哥Sun */
public class Demo06 {public static void main(String[] args) {List<String> names = new ArrayList<String>();List<Integer> ages = new ArrayList<Integer>();List<Number> numbers = new ArrayList<Number>();names.add("一一哥");names.add("秦始皇");ages.add(28);ages.add(50);ages.add(28);numbers.add(100);numbers.add(800);//String等非Number类型就不行//printElementUpbound(names);//泛型值只能是Number及其子类类型,所以Integer/Double/Float等类型都可以,但String就不行printElementUpbound(ages);printElementUpbound(numbers);}//上限通配符的使用,这里的泛型值只能是Number及其子类类型public static void printElementUpbound(List<? extends Number> data) {for(int i=0;i<data.size();i++) {//data.getClass().getSimpleName():用于获取某个类的类名System.out.println(data.getClass().getSimpleName()+"--data: " + data.get(i));}}
}

在这个例子中,printElementUpbound方法中的集合泛型,可以是Number类或其子类,除此之外的其他类型都不行。也就是说,我们只能使用Number或其子类作为类型参数,泛型类型的上限是Number,这就是上限通配符的含义。

6. 下限通配符(? super T)

下限通配符(?super T)是一种表示类型下限的通配符,其中T是一个类或接口。它指定了可以使用的类型下限,主要用于限制输出的参数类型。下面是一个简单的例子:

import java.util.ArrayList;
import java.util.List;public class Demo07 {public static void main(String[] args) {List<String> names = new ArrayList<String>();List<Integer> ages = new ArrayList<Integer>();List<Double> numbers = new ArrayList<Double>();names.add("一一哥");names.add("秦始皇");ages.add(28);ages.add(50);ages.add(28);numbers.add(100.0);numbers.add(800.9);//String等非Number类型就不行//printElementUpbound(names);//此时Double类型也不行//printElementDownbound(numbers);//泛型值只能是Integer及其父类类型,所以Double/Float/String等类型都不可以printElementDownbound(ages);}//下限通配符的使用,这里的泛型值只能是Integer及其父类类型public static void printElementDownbound(List<? super Integer> data) {for(int i=0;i<data.size();i++) {System.out.println(data.getClass().getSimpleName()+"--data: " + data.get(i));}}
}

在这个例子中,printElementDownbound方法中的集合泛型,可以是Integer或其父类型,即类型下限是Integer,除此之外的其他类型都不行。也就是说,我们只能使用Integer或其父类作为类型参数,泛型类型的下限是Integer,这就是下限通配符的含义。

7. <? extends T>和<? super T>的区别

在这里,要给大家再总结一下<? extends T>和<? super T>的区别:

  • <? extends T> 允许调用 T get()这样的 读方法来获取 T对象 的引用,但不允许调用 set(T)这样的 写方法来传入 T 的引用(传入 null 除外);
  • <? super T> 允许调用 set(T)这样的 写方法传入 T对象 的引用,但不允许调用 T get()这样的 读方法来获取 T对象 的引用(获取 Object 除外)。
  • <? extends T> 允许读不允许写, <? super T> 允许写不允许读。

大家注意,无论是未限定通配符、上限通配符还是下限通配符,我们既可以在方法中使用,也可以在类或接口中使用。

三. 泛型擦除

我们在学习泛型时,除了要掌握上面这些泛型类、泛型接口、泛型方法以及通配符等内容之外,还要学习泛型擦除的相关内容。那么什么是泛型擦除呢?我们继续往下学习吧。

1. 简介

所谓的泛型擦除(Type Erasure),就是指在编译时,JVM编译器会将所有的泛型信息都擦除掉,变成原始类型,一般是将泛型的类型参数替换成具体类型的上限或下限(如果没有指定上界,则默认为Object)。

换句话说,虽然我们在代码中使用了泛型,但在编译后,所有的泛型类型都会被擦除掉,转而使用其对应的原始类型。这就是Java泛型的底层实现原理。这样设计的目的是为了兼容旧的JDK版本,使得Java具有了较好的向后兼容性,旧的非泛型代码可以直接使用泛型类库,而不需要进行任何修改。同时,Java也提供了反射机制来操作泛型类型,使得泛型类型在某些情况下还是可以被获取到的,所以即使有泛型擦除,仍然也不会太影响Java虚拟机的运行时效率。

比如,在我们定义一个泛型类时,我们会使用泛型类型参数来代替具体的类型,好比下面这个例子:
在这里插入图片描述

2. 泛型擦除带来的限制

在编译之后,这个泛型类的类型参数T就会被擦除,成为其对应的原始类型Object。

这也意味着,我们无法在运行时获取到泛型的实际类型参数,所以泛型擦除的使用会有一些限制。首先由于泛型类型参数被擦除了,因此我们在运行时就无法获得泛型类型参数的信息。例如,如果我们有一个List类型的变量,在运行时我们就无法获得这个List集合中的元素类型是Integer。另一个限制是在使用泛型类型时,还需要注意类型安全性。在编译阶段,编译器会检查泛型类型的类型安全性;但在运行阶段,由于泛型类型参数被擦除了,因此就无法保证类型安全性了。泛型擦除的限制,主要表现在以下几个方面:

无法使用基本类型实例化类型参数;

无法在运行时获取泛型类型信息;

泛型类型参数不能用于静态变量或静态方法;

不能实例化T类型。

接下来再给大家具体分析一下这些限制

2.1 无法使用基本类型实例化类型参数

Java泛型中的类型参数不能是基本类型,只能是类或接口类型。例如,以下代码在编译阶段会出错,无法通过编译:

List<int> list = new ArrayList<int>();

正确的写法是使用基本类型对应的包装类型,如下所示:

List<Integer> list = new ArrayList<Integer>();

2.2 无法在运行时获取泛型类型信息

由于泛型擦除的存在,导致我们在程序运行时无法获取泛型类型的信息。例如,以下代码在运行时就无法获取List的元素类型:

List<String> list = new ArrayList<String>(); 
Class<?> clazz = list.getClass(); 
Type type = clazz.getGenericSuperclass(); 
// 输出:class java.util.ArrayList<E>
System.out.println(type); 

在输出的结果中,我们只能得到ArrayList的类型信息,而无法获取到集合中具体的泛型类型信息,也就是获取不到String的信息。但如果我们就是想在运行时获取到泛型的实际类型参数,其实可以参考以下方式进行实现:

public class Box<T> {private T content;public Box(T content) {this.content = content;}public T getContent() {return content;}public void setContent(T content) {this.content = content;}public Class<?> getContentType() {return content.getClass();}
}

在上面的代码中,我们新增了一个方法 getContentType(),该方法用于返回泛型类型参数的实际字节码类型,以后我们通过调用这个方法,就可以间接地获取到泛型类型的信息了。

2.3 泛型类型参数不能用于静态变量或静态方法

由于泛型类型参数是在实例化对象时才被确定的,因此不能在静态变量或静态方法中使用泛型类型参数。例如,以下代码是无法编译通过的:

public class MyClass<T> {   //这样的代码编译不通过private static T value;   public static void setValue(T value) {        MyClass.value = value;     } 
}

正确的写法是使用一个实际类型来代替泛型类型参数:

public class MyClass {     private static String value;          public static void setValue(String value) {         MyClass.value = value;     } 
}

2.4 不能实例化T类型

比如在下面的案例中:

public class MyClass<T> {private T first;private T last;public MyClass() {first = new T();last = new T();}
}

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

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

擦拭后实际上变成了:

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

这样一来,创建new MyClass<String>()和创建new MyClass<Integer>()就变成了Object,编译器会阻止这种类型不对的代码。如果我们想对泛型T进行实例化,需要借助Class< T >参数并集合反射技术来实现,且在使用时也必须传入Class< T >。


四. 结语

不过,尽管泛型擦除有一些限制,但泛型仍然不失为一种强大的编程工具,它可以提高代码的可读性和可维护性。通过合理地使用泛型,我们可以在编译时进行类型检查,避免类型转换的错误和运行时异常,从而提高了代码的安全性和可靠性。同时,我们也需要了解Java泛型擦除的限制,以便在实际应用中做出正确的决策。

视频教程:视频教程,戳我直达


http://chatgpt.dhexx.cn/article/i1eOdfLT.shtml

相关文章

军职在线大学计算机基础(自主模式),《军职在线》中国哲学经典著作导读(自主模式)...

《军职在线》中国哲学经典着作导读(自主模式) 1《周易》导读 测试题C A C B D C D B B D D A B C D C D B A B 2《道德经》导读 测试题D C D C C C D A D C D B C C C A B A A B 3《庄子》导读 测试题D C D A A C B A A C A A C A D C B 4《论语》导读 测试题B B C ED B A A A …

台湾国立大学(林轩田)《机器学习技法》(第7讲)blending and bagging

课程地址&#xff1a;https://class.coursera.org/ntumlone-001/class 课件讲义&#xff1a;http://download.csdn.net/download/malele4th/10212756 注明&#xff1a;文中图片来自《机器学习技法》课程和部分博客 建议&#xff1a;建议读者学习林轩田老师原课程&#xff0c…

大学有新民之道,则大学生者负新民工作之实际责任者也。

梅贻琦&#xff0c;&#xff08;1889-1962&#xff09;&#xff0c;字月涵&#xff0c;为梅曾臣长子。自1914年由美国吴士脱大学学成归国&#xff0c;即到清华担任教学和教务长等多种职务。1931年&#xff0c;梅贻琦出任清华校长&#xff0c;自此后一直到他在台湾去世&#xff…

【转载】中庸与技术书

2019独角兽企业重金招聘Python工程师标准>>> 转自&#xff1a;图灵社区 原文作者&#xff1a;刘祺 原文地址&#xff1a;http://www.ituring.com.cn/article/213657 本次转载已经过原作者同意&#xff0c;二次转载请自行联系原作者 #中庸与技术书 在我写这篇文章之前…

《大学》《中庸》全文及翻译 (转载)

《大学》全文及翻译 原文&#xff1a; 大学之道&#xff0c;在明明德&#xff0c;在亲民&#xff0c;在止于至善。知止而后有定&#xff0c;定而后能静&#xff0c;静而后能安&#xff0c;安而后能虑&#xff0c;虑而后能得。物有本末&#xff0c;事有终始&#xff0c;知所先后…

[C语言]求一个数是否是2的n次方

设a8&#xff0c;a的二进制数为1000&#xff0c;若为16&#xff0c;则是 0001 0000&#xff0c;2的n次方转为二进制则只保留一个 1 &#xff0c; 其余位置全是0&#xff0c;因此只要判断这个数的二进制是否只有一个 1 &#xff0c;则知道这个数是否是2的n次方。 //求一个数是…

C语言|s1-s0|<=10的-6次方

#include <stdio.h> #include <math.h> double fun(double x) { double s11.0,s00.0; double t1.0; int n1; do { s0s1;//此时s0为s1的上一项 tt*(0.5-n1)*x/n; s1s1t; n; } while(fabs(s1-s0)>1e…

c语言字母能乘10吗,c语言编程中表示a乘以10的n次幂怎么表示

可以参考下面的代码&#xff1a; #include int main() { float a,s,n; sa*mi(10,n); return 0; } float mi(float x,int y) { float a; int i; a1; if(y>0) { for(i1;i<y;i) { aa*x; } } else { for(i-1;i>y;i--) { aa/x; } } return a; } 扩展资料&#xff1a; C语言…

c语言学习-编写函数求x的n次方的值

编写函数求x的n次方的值 程序流程图&#xff1a; 代码&#xff1a; #include<stdio.h> long mul(int j ,int k) { int i; long mu1; for(i0;i<k;i) mumu*j; return mu; } void main() { int x,n; long m; printf("please enter x\tn\t"); scanf("%…

c语言x的n次方怎么写_C语言入门教程(三)进制与操作符

Hello,小伙伴们大家好,今天开始进入C语言第三课时的学习。进入今天正式内容之前呢,先来看一下上次课程中的留下的一道练习题。 练习: 1.编写程序计算半径为任意浮点数的圆周长并把结果打印在屏幕上 1、进制 一个字节分成八段,每段只能记录一个0或者1要想把一个数字记录在一…

c语言n次方怎么输入_C语言中10个经典的算法,学会它,利用它

C语言中有有许多经典的算法,这些算法都是许多人的智慧结晶,也是编程中常用的算法,这里面包含了众多算法思想,掌握这些算法,对于学习更高级的、更难的算法都会有很大的帮助,会为自己的算法学习打下坚实的基础。 接下来我们先来看10道: (1) 输出9*9乘法口诀 运行结果: (2…

c语言编程如何进行n次方运算,c语言n次方怎么输入?_后端开发

python编程如何求2000到2500闰年&#xff1f;_后端开发 python编程求2000到2500闰年的方法&#xff1a;首先定义年份【i2000】&#xff1b;然后用while循环判断是否在2500内&#xff1b;接着若年份可被40整除且不被100整除&#xff0c;则是闰年&#xff0c;若年份可被400整除&a…

C语言怎么编辑次方,c语言怎么表示一个数的n次方

2009-01-05 C语言问题从键盘输入8个数,用选择法按由大到小的循序排列并输出,要求用指针来实现。 你应该自己实现这个小程序。 例子,单向链表排序(冒泡): struct student {int num; struct student *next; } struct student *paixu(struct student *head) {struct student *p,…

c语言中e的n次方怎么打,C语言中N次方怎么打

满意答案 lawq0364t 2020.05.11 采纳率&#xff1a;52% 等级&#xff1a;7 已帮助&#xff1a;2761人 有两个函数可以实现&#xff0c;double pow(double x, double y)&#xff0c;double pow10(int p) 下面是这两个函数的使用方法&#xff0c;个人建议用&#xff1a;pow10(…

c语言10的10万次方,在c语言编程中 10的n次方应该怎么表达

满意答案 lvoeshg99 推荐于 2017.09.13 采纳率&#xff1a;56% 等级&#xff1a;9 已帮助&#xff1a;461人 在C语言中10的n次方表示&#xff1a;10^n&#xff0c;或者使用函数&#xff1a;pow(10&#xff0c;n)和pow10(n)。 C语言的幂运算是很耗资源的&#xff0c;10的3次方…

c语言表达式的次方怎么表示,在C语言中,10的n次方怎么表示?

有两个函数可以实现&#xff0c;double pow(double x, double y)&#xff0c;double pow10(int p) 下面是这两个函数的使用方法&#xff0c;个人建议用&#xff1a;pow10(n) 函数名: pow 功 能: 指数函数(x的y次方) 用 法: double pow(double x, double y); 程序例: #include…

最好用的pdf阅读软件 Acrobat Reader DC安装教程(无需破解)

下载在线安装的小文件 直通车 若无法打开&#xff08;FQ&#xff09;可以下载我已经下载好了的 https://pan.baidu.com/s/1jzd8CnB4sHLCCwQvztLmYg 就这个小文件&#xff0c;双击便会在线下载安装。默认安装在C盘 由于我已经安装了&#xff0c;所以只是检测我的是否为最新…

Adobe Acrobat中操作pdf文件被保护,请输入许可口令

文档目录 问题描述解决步骤口令破解工具下载链接 问题描述 Adobe Acrobat中操作pdf时出现此问题&#xff1a; 使用口令破解工具将此pdf破解后方可正常使用。 解决步骤 1、下载PDF Password Remover后将其解压&#xff0c;然后双击PPR.exe 2、选择输出目录&#xff0c;然后…

Adobe的PDF工具Acrobat Pro DC 2023版本下载与安装

目录 前言一、Acrobat Pro DC安装二、使用配置总结 前言 Acrobat Pro DC提供了一系列功能和特性&#xff0c;用于创建、编辑、组织和共享PDF文件。注&#xff1a;文末附有下载链接&#xff01; 一、Acrobat Pro DC安装 1、运行安装程序&#xff0c;如图所示。 2、选择自定义&…

PDF加密、解密、破解和转换软件

本人因下载了一PDF文档&#xff0c;日语的&#xff0c;像转成DOC格式的&#xff0c;从网上找了PDF转DOC软件&#xff0c;发现转换不了&#xff0c;要口令&#xff0c;所以又找破解PDF软件&#xff0c;一次搞定。从网上找的时候都没有一篇完整的文章&#xff0c;所以就有了下文。…