当前位置: 首页 > news >正文

Java 泛型知识点

        在 Java 编程中,泛型(Generics)是一项至关重要的特性,它从 JDK 5 开始引入,彻底改变了代码的类型安全和复用性。泛型的核心思想是 “参数化类型”,即允许在定义类、接口和方法时,将类型作为参数传入,从而实现代码的通用化,同时在编译阶段就能检测出类型不匹配的错误,避免运行时异常。本文将从基础到进阶,全面拆解 Java 泛型的知识点。

一、泛型的核心价值:为什么需要泛型?

在泛型出现之前,Java 开发者通常使用Object类来实现通用代码。例如,要实现一个存储任意类型数据的集合,会这样写:

// 无泛型时代的集合
List list = new ArrayList();
list.add("Java");       // 存入String
list.add(123);          // 存入Integer
list.add(new Object()); // 存入Object// 取值时必须强制类型转换
String str = (String) list.get(0); // 正常转换
Integer num = (Integer) list.get(1); // 正常转换
String error = (String) list.get(2); // 运行时异常:ClassCastException

这种写法存在两大问题:

  1. 类型不安全:集合可以存入任意类型的数据,编译器无法检查类型,只能在运行时通过强制转换暴露错误,增加了程序崩溃的风险。
  1. 代码冗余:每次取值都需要手动强制类型转换,代码繁琐且易出错。

泛型的出现正是为了解决这些问题,它带来了三大核心优势:

  • 编译期类型检查:在定义集合时指定类型,编译器会自动拦截不符合类型的数据存入,从源头避免类型错误。
  • 消除强制类型转换:取值时无需手动转换,编译器会根据泛型参数自动推断类型,代码更简洁。
  • 代码复用:一套泛型代码可以适配多种数据类型,无需为不同类型重复编写逻辑(如不同类型的集合、工具类)。

二、泛型的基础语法:如何定义和使用泛型?

泛型的语法核心是 “类型参数”,通常用大写字母表示(如T、E、K、V等,遵循约定俗成的命名:T=Type,E=Element,K=Key,V=Value)。根据使用场景,泛型可分为泛型类泛型方法泛型接口三类。

(一)泛型类:将类型参数绑定到类上

泛型类是指在类定义时声明类型参数,使得类中的属性、方法参数或返回值可以使用该类型参数。其语法格式为:

// 定义泛型类:<类型参数> 放在类名后
class 类名<类型参数1, 类型参数2, ...> {// 类型参数可用于属性、方法参数、返回值private 类型参数1 属性名;public 类型参数1 方法名(类型参数2 参数名) {return 属性名;}
}

实例:实现一个通用的 “数据包装类”

假设需要一个类来包装不同类型的数据(如 String、Integer、User 对象),使用泛型类可以一次实现:

// 泛型类:T为类型参数,表示“任意类型”
class DataWrapper<T> {private T data; // 使用T作为属性类型// 构造器:参数类型为Tpublic DataWrapper(T data) {this.data = data;}// 方法:返回值类型为Tpublic T getData() {return data;}// 方法:参数类型为Tpublic void setData(T data) {this.data = data;}// 通用方法:打印数据(无需关心T的具体类型)public void printData() {System.out.println("包装的数据:" + data);}
}// 使用泛型类
public class GenericClassDemo {public static void main(String[] args) {// 1. 包装String类型:指定T为StringDataWrapper<String> stringWrapper = new DataWrapper<>("Hello Generics");String strData = stringWrapper.getData(); // 无需强制转换stringWrapper.printData(); // 输出:包装的数据:Hello Generics// 2. 包装Integer类型:指定T为IntegerDataWrapper<Integer> intWrapper = new DataWrapper<>(100);Integer intData = intWrapper.getData();intWrapper.printData(); // 输出:包装的数据:100// 3. 错误场景:存入不符合类型的数据会编译报错// intWrapper.setData("abc"); // 编译错误:不兼容的类型,无法将String转换为Integer}
}

注意:泛型类在创建对象时必须指定具体类型(如DataWrapper<String>),JDK 7 + 支持 “菱形语法”(new DataWrapper<>()),编译器会自动推断类型参数。

(二)泛型方法:将类型参数绑定到方法上

泛型方法是指在方法定义时单独声明类型参数,即使所在的类不是泛型类,也可以定义泛型方法。其语法格式为:

// 泛型方法:<类型参数> 放在返回值类型前
修饰符 <类型参数1, 类型参数2, ...> 返回值类型 方法名(参数列表) {// 方法体中使用类型参数
}

实例:实现一个通用的 “数组转集合” 方法

不同类型的数组(如String[]、Integer[])需要转换为集合时,泛型方法可以统一处理:

import java.util.ArrayList;
import java.util.List;public class GenericMethodDemo {// 泛型方法:T为类型参数,接收T[]数组,返回List<T>public static <T> List<T> arrayToList(T[] array) {List<T> list = new ArrayList<>();for (T element : array) {list.add(element);}return list;}public static void main(String[] args) {// 1. String数组转List<String>String[] strArray = {"Java", "Generics", "Demo"};List<String> strList = arrayToList(strArray);System.out.println("String列表:" + strList); // 输出:[Java, Generics, Demo]// 2. Integer数组转List<Integer>Integer[] intArray = {1, 2, 3, 4};List<Integer> intList = arrayToList(intArray);System.out.println("Integer列表:" + intList); // 输出:[1, 2, 3, 4]// 3. 自动推断类型:无需显式指定<T>(JDK 7+支持)// 若显式指定,写法为:List<Double> doubleList = GenericMethodDemo.<Double>arrayToList(doubleArray);Double[] doubleArray = {1.1, 2.2};List<Double> doubleList = arrayToList(doubleArray);}
}

泛型方法的关键是类型参数声明在方法返回值之前,这是它与泛型类中方法的核心区别(泛型类的方法使用的是类的类型参数)。

(三)泛型接口:将类型参数绑定到接口上

泛型接口与泛型类类似,在接口定义时声明类型参数,实现接口的类需要指定具体类型,或继续保留类型参数(成为泛型类)。其语法格式为:

// 定义泛型接口:<类型参数> 放在接口名后
interface 接口名<类型参数1, 类型参数2, ...> {// 类型参数可用于方法返回值或参数类型参数1 方法名(类型参数2 参数名);
}

实例:实现一个通用的 “数据处理器” 接口

假设需要处理不同类型的数据(如 String、User),定义泛型接口后,不同实现类可针对性处理:

// 泛型接口:T为待处理的数据类型
interface DataProcessor<T> {// 处理数据的方法:接收T类型,返回T类型(处理后结果)T process(T data);
}// 实现1:处理String类型(转为大写)
class StringProcessor implements DataProcessor<String> {@Overridepublic String process(String data) {return data.toUpperCase(); // 输入"hello",输出"HELLO"}
}// 实现2:处理Integer类型(加10)
class IntegerProcessor implements DataProcessor<Integer> {@Overridepublic Integer process(Integer data) {return data + 10; // 输入20,输出30}
}// 使用泛型接口
public class GenericInterfaceDemo {public static void main(String[] args) {DataProcessor<String> stringProcessor = new StringProcessor();System.out.println(stringProcessor.process("hello")); // 输出:HELLODataProcessor<Integer> integerProcessor = new IntegerProcessor();System.out.println(integerProcessor.process(20)); // 输出:30}
}

注意:若实现类不想指定具体类型,可继续保留泛型参数(如class UserProcessor<T> implements DataProcessor<T>),此时实现类也是泛型类。

三、泛型的进阶特性:边界、通配符与类型擦除

掌握基础语法后,还需要理解泛型的进阶特性,包括泛型边界(限制类型参数的范围)、通配符(灵活处理未知类型)和类型擦除(泛型的底层实现原理)。

(一)泛型边界:限制类型参数的范围

默认情况下,泛型的类型参数可以是任意类(如T可以是String、Integer、User等)。但有时需要限制类型参数的范围(如仅允许 “Number 的子类” 或 “实现了 Serializable 接口的类”),这就需要用到泛型边界,通过extends关键字实现(注意:泛型中extends既表示 “继承类”,也表示 “实现接口”)。

语法格式:

// 单个边界:T必须是BoundClass的子类或实现类
class 类名<T extends BoundClass> { ... }// 多个边界:T必须同时满足所有边界(类在前,接口在后,用&分隔)
class 类名<T extends BoundClass & BoundInterface1 & BoundInterface2> { ... }

实例 1:限制类型参数为 “Number 的子类”

假设需要一个计算 “数值总和” 的工具类,仅允许处理数字类型(如 Integer、Double),可限制T extends Number:

// 泛型类:T必须是Number的子类(如Integer、Double)
class NumberSumCalculator<T extends Number> {private List<T> numbers;public NumberSumCalculator(List<T> numbers) {this.numbers = numbers;}// 计算总和:利用Number的doubleValue()方法(所有子类都实现了该方法)public double calculateSum() {double sum = 0.0;for (T num : numbers) {sum += num.doubleValue(); // 若没有边界限制,num无法调用doubleValue()}return sum;}
}// 使用
public class GenericBoundDemo1 {public static void main(String[] args) {// 1. 处理Integer列表List<Integer> intList = List.of(1, 2, 3);NumberSumCalculator<Integer> intCalculator = new NumberSumCalculator<>(intList);System.out.println("Integer总和:" + intCalculator.calculateSum()); // 输出:6.0// 2. 处理Double列表List<Double> doubleList = List.of(1.5, 2.5, 3.5);NumberSumCalculator<Double> doubleCalculator = new NumberSumCalculator<>(doubleList);System.out.println("Double总和:" + doubleCalculator.calculateSum()); // 输出:7.5// 3. 错误场景:传入非Number类型(如String)会编译报错// List<String> strList = List.of("1", "2");// NumberSumCalculator<String> strCalculator = new NumberSumCalculator<>(strList); // 编译错误}
}

实例 2:多个边界(类 + 接口)

限制类型参数同时是 “User 的子类” 且 “实现了 Comparable 接口”:

class User {private String name;// 省略构造器、getter、setter
}// 泛型类:T必须是User的子类,且实现Comparable接口
class UserSorter<T extends User & Comparable<T>> {public void sort(List<T> users) {users.sort((u1, u2) -> u1.compareTo(u2)); // 因T实现Comparable,可调用compareTo()}
}

(二)通配符:灵活处理未知的泛型类型

当需要处理 “未知类型的泛型” 时(如接收任意类型的List),可以使用通配符(?)。通配符分为三种:无界通配符、上界通配符、下界通配符。

1. 无界通配符(?):表示 “任意类型”

适用于仅需要读取泛型对象,且不关心具体类型的场景(如打印任意List的内容)。

// 无界通配符:接收任意类型的List
public static void printList(List<?> list) {for (Object obj : list) {System.out.print(obj + " ");}System.out.println();
}// 使用
public class WildcardDemo1 {public static void main(String[] args) {List<String> strList = List.of("A", "B", "C");List<Integer> intList = List.of(1, 2, 3);List<User> userList = List.of(new User("Alice"), new User("Bob"));// 同一方法处理不同类型的ListprintList(strList);   // 输出:A B C printList(intList);   // 输出:1 2 3 printList(userList);  // 输出:User{name='Alice'} User{name='Bob'} }
}

注意:无界通配符的List<?>与List<Object>不同:

  • List<Object>可以存入任意类型的数据(如list.add("a")、list.add(1));
  • List<?>表示 “未知类型的 List”,编译器无法确定类型,因此不允许添加非 null 的数据(仅允许list.add(null)),只能读取(读取到的是Object类型)。

2. 上界通配符(? extends 边界):表示 “边界的子类或实现类”

适用于需要读取泛型对象,且类型范围有限制的场景(如读取 “Number 子类的 List”)。

// 上界通配符:接收Number子类的List(如Integer、Double)
public static double sumNumberList(List<? extends Number> list) {double sum = 0.0;for (Number num : list) {sum += num.doubleValue(); // 因类型是Number子类,可调用doubleValue()}return sum;
}// 使用
public class WildcardDemo2 {public static void main(String[] args) {List<Integer> intList = List.of(1, 2, 3);List<Double> doubleList = List.of(1.5, 2.5);System.out.println(sumNumberList(intList));  // 输出:6.0System.out.println(sumNumberList(doubleList));// 输出:4.0// 错误场景:传入非Number子类的List(如String)会编译报错// List<String> strList = List.of("1", "2");// sumNumberList(strList); // 编译错误}
}

注意:上界通配符List<? extends Number>同样不允许添加数据(例如,无法确定 List 是Integer还是Double,添加1或1.5都可能出错),仅支持读取。

3. 下界通配符(? super 边界):表示 “边界的父类或超类”

适用于需要向泛型对象中添加数据,且类型范围有限制的场景(如向 “Integer 父类的 List” 中添加 Integer)。

// 下界通配符:接收Integer父类的List(如Integer、Number、Object)
public static void addIntegers(List<? super Integer> list) {list.add(10);   // 允许添加Integer类型(因父类List可接收子类对象)
</doubaocanvas>

 

 


文章转载自:

http://sxe3CyTN.wqpqk.cn
http://rr1L48H2.wqpqk.cn
http://rN380NgI.wqpqk.cn
http://NcSvVgwu.wqpqk.cn
http://a99Dfltd.wqpqk.cn
http://tAKQC9NL.wqpqk.cn
http://9ezU9qDm.wqpqk.cn
http://DU0QuOwb.wqpqk.cn
http://0OaIAOSD.wqpqk.cn
http://BcLQHmvv.wqpqk.cn
http://7Xwh58yy.wqpqk.cn
http://YiUzTlGO.wqpqk.cn
http://ORQ9x2IC.wqpqk.cn
http://UVweWd4W.wqpqk.cn
http://wE7aSDYg.wqpqk.cn
http://mRRUUf2u.wqpqk.cn
http://NUiOln0Q.wqpqk.cn
http://mMnQ4yjR.wqpqk.cn
http://xtuBNrR9.wqpqk.cn
http://4MnlPNhZ.wqpqk.cn
http://ghRiN8S4.wqpqk.cn
http://tndIfIRj.wqpqk.cn
http://Fii2h4M5.wqpqk.cn
http://Zqo69wTX.wqpqk.cn
http://O0x268zy.wqpqk.cn
http://XAg8Oc9T.wqpqk.cn
http://coXh1ETb.wqpqk.cn
http://gxeryRxw.wqpqk.cn
http://ToSxRnqS.wqpqk.cn
http://PBTvzguL.wqpqk.cn
http://www.dtcms.com/a/373755.html

相关文章:

  • 天硕工业级SSD固态硬盘凭什么寿命更长?
  • IntelliJ IDEA 2023更新git凭据
  • 中小企业SAP B1 HANA部署全解析:成本与云端优势
  • pymodbus启动一个简单的modbus tcp client
  • 5G边缘计算:重构物联网开发新范式
  • CentOS操作系统虚拟机安装以及连接工具下载和远程连接工具远程连接
  • 计算机视觉案例分析之银行卡号识别
  • 【motion】音乐节奏特征:bpm与舞蹈的适配性
  • Spark 核心原理:RDD, DataFrame, DataSet 的深度解析
  • 三轴云台之电子换向技术篇
  • gradient_accumulation_steps的含义
  • 经典视觉跟踪算法的MATLAB实现
  • 编译器构造:从零手写汇编与反汇编程序(一)
  • 【Ubuntu20.04 + VS code 1.103.2 最新版,中文输入法失效】
  • 【开题答辩全过程】以 基于Python的北城公务用车系统设计与实现_为例,包含答辩的问题和答案
  • Proximal SFT:用PPO强化学习机制优化SFT,让大模型训练更稳定
  • 2025年Q3 GEO优化供应商技术能力评估与行业应用指南
  • 25上半年软考网工备考心得
  • XPath:从入门到能用
  • Kotlin协程 -> Job.join() 完整流程图与核心源码分析
  • [优选算法专题二滑动窗口——串联所有单词的子串]
  • VR森林防火模拟进行零风险演练,成本降低​
  • 玩转Docker | 使用Docker部署Kener状态页监控工具
  • Oracle 官网账号登不了?考过的证书还能下载吗?
  • Oracle 数据库高级查询语句方法
  • WSD3075DN56高性能MOS管在汽车电动助力转向系统(EPS)中的应用
  • 1.1 汽车运行滚动阻力
  • LinuxC++项目开发日志——高并发内存池(3-thread cache框架开发)
  • Android 自定义 TagView
  • 下沉一线强赋能!晓商圈多维帮扶护航城市共建者