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

Java基础夯实——泛型全解析

泛型是一种允许定义类,接口,方法时不预先加载指定的具体类型,而是使用时确定的一种特性

目录

使用情景

泛型类

泛型接口

泛型方法

使用规范

不能使用基本数据类型

不能在静态字段和方法中使用泛型

通配符与边界

无界通配符:? 表示位置类型

上界通配符:?extends 类型 表示限定指定的泛型类型必须是extends关键字后类型或其子类

下界通配符:?super 类型 表示限定指定的泛型类型必须是super关键字后类型或其父类

核心作用

类型安全

消除强制类型转换

代码复用

实现机制:泛型擦除

擦除过程核心规则(技术原理)

无界类型参数:直接在编译阶段被擦除为Object

上界单一边界类型参数:擦除为上界类型

上界多边界类型参数:擦除为第一个边界

下界类型参数:擦除为最具体的合法类型

桥接方法生成机制

擦除机制导致的影响

泛型类型在运行时不能访问

无法创建泛型实例或数组


使用情景

泛型类

泛型类是指带有类型参数的类

//泛型类的创建
public class 类名<泛型类型1,泛型类型2,……>{//类的成员
}    

 其中,<>中的泛型类型可以有多个,用逗号分隔。这些泛型类型在类被使用时会被具体的类型所替代

//泛型类——例
//泛型类定义
public class Box<T> {private T content;public void set(T content) {this.content = content;}public T get() {return content;}
}
//泛型类使用
Box<String> stringBox = new Box<>();
stringBox.set("Hello");
String content = stringBox.get(); // 无需强制类型转换Box<Integer> integerBox = new Box<>();
integerBox.set(100);
Integer number = integerBox.get(); // 无需强制类型转换

泛型接口

泛型接口是指带有参数类型的接口

//泛型接口的创建
public interface 接口名<泛型类型1,泛型类型2,……>{//    接口方法
}    

在实现泛型接口时可以指定泛型类型,也可以保留泛型,用泛型类实现泛型接口

//泛型接口——例
//泛型接口定义
public interface Generator<T> {T generate();
}
//泛型接口指定泛型类型的实现类
public class StringGenerator implements Generator<String> {@Overridepublic String generate() {return "Generated String";}
}
//泛型接口保留泛型的实现类
public class NumberGenerator<T extends Number> implements Generator<T> {private T number;public NumberGenerator(T number) {this.number = number;}@Overridepublic T generate() {return number;}
}

泛型方法

泛型方法是指使用泛型参数的方法,可以在普通类或者泛型类中定义,也可以在泛型类中定义

//泛型方法的定义
public <泛型类型1, 泛型类型2, ...> 返回类型 方法名(参数列表) {// 方法体
}

泛型方法会在调用时指定具体的类型

//泛型方法——例
public class GenericMethodExample {public static <T> void printArray(T[] array) {for (T element : array) {System.out.print(element + " ");}System.out.println();}public static void main(String[] args) {Integer[] intArray = {1, 2, 3, 4, 5};String[] stringArray = {"Hello", "World"};printArray(intArray); // 输出: 1 2 3 4 5printArray(stringArray); // 输出: Hello World}
}

使用规范

不能使用基本数据类型

泛型类型的参数不能是基本数据类型,必须使用应用数据类型(int等基本数据类型需要使用对应的包装类,如Integer)

不能在静态字段和方法中使用泛型

由于泛型类和类的泛型字段都需要在实例化的时候给定,在静态字段或者方法中使用泛型的话可能在完全没有给定具体类型的情况下调用方法或字段,明显不符合泛型设计,所以被禁止使用

通配符与边界

类型通配符是在使用泛型时表示未知类型的一种方式,用 ? 表示

配合 extends 或者 super 关键字可以对其上界或者下界进行限定,主要有三种形式:

无界通配符:? 表示位置类型
上界通配符:?extends 类型 表示限定指定的泛型类型必须是extends关键字后类型或其子类
下界通配符:?super 类型 表示限定指定的泛型类型必须是super关键字后类型或其父类
//通配符使用——例
import java.util.ArrayList;
import java.util.List;public class WildcardExample {// 无界通配符public static void printList(List<?> list) {for (Object element : list) {System.out.print(element + " ");}System.out.println();}// 上界通配符public static double sumOfList(List<? extends Number> list) {double sum = 0.0;for (Number n : list) {sum += n.doubleValue();}return sum;}// 下界通配符public static void addNumbers(List<? super Integer> list) {for (int i = 1; i <= 5; i++) {list.add(i);}}public static void main(String[] args) {List<Integer> intList = List.of(1, 2, 3);List<Double> doubleList = List.of(1.5, 2.5, 3.5);printList(intList); // 输出: 1 2 3printList(doubleList); // 输出: 1.5 2.5 3.5System.out.println(sumOfList(intList)); // 输出: 6.0System.out.println(sumOfList(doubleList)); // 输出: 7.5List<Object> objectList = new ArrayList<>();addNumbers(objectList);System.out.println(objectList); // 输出: [1, 2, 3, 4, 5]}
}

核心作用

类型安全

通过类型参数化把类型检查提前到编译阶段,确保只允许正确类型的对象被添加到泛型集合或传递给泛型方法,减少运行时会出现的ClassCastException异常

//类型安全——例
List<String> list = new ArrayList<>();
list.add("hello");
// list.add(123); // 编译错误:无法将 Integer 放入 List<String>
String str = list.get(0); // 无需强制转换,编译时已确保类型安全

消除强制类型转换

泛型允许编译器自动推断类型,避免手动编写强制类型转换代码

类型参数在编译后被擦除,但编译器会在生成字节码时插入必要的类型转换

//消除强制类型转换——例
Map<String, String> map = new HashMap<>();
map.put("key", "value");
String value = map.get("key"); // 无需强制转换,编译器自动处理

代码复用

泛型通过参数化类型实现通用算法,使同一个类或方法可以处理多种数据类型,无需为每种类型重复编写代码

//代码复用——例
//通用数组交换方法例
public static <T> void swap(T[] array, int i, int j) {T temp = array[i];array[i] = array[j];array[j] = temp;
}
// 使用示例
Integer[] intArray = {1, 2, 3};
swap(intArray, 0, 2); // 交换 Integer 数组
String[] strArray = {"a", "b", "c"};
swap(strArray, 0, 2); // 同一方法处理 String 数组//通用泛型实现容器
public class Box<T> {private T content;public void set(T content) { this.content = content; }public T get() { return content; }
}
// 使用示例
Box<Integer> intBox = new Box<>();
intBox.set(100);
Box<String> strBox = new Box<>();
strBox.set("hello");

实现机制:泛型擦除

泛型擦除是Java泛型实现的核心机制,它在编译阶段移除了所有泛型的类型信息,使代码在运行使与非泛型版本保持兼容

擦除过程核心规则(技术原理)

无界类型参数:直接在编译阶段被擦除为Object
//无界类型参数擦除
class Box<T> {private T value; // 擦除后变为 private Object value;
}
上界单一边界类型参数:擦除为上界类型
//上界类型参数擦除
class Box<T extends Number> {private T value; // 擦除后变为 private Number value;
}
上界多边界类型参数:擦除为第一个边界
//多边形类型参数擦除
class Box<T extends Comparable<T> & Serializable> {private T value; // 擦除后变为 private Comparable<T> value;
}
下界类型参数:擦除为最具体的合法类型
//下界类型参数擦除
void addNumbers(List<? super Integer> list) {list.add(10); // 擦除后:list.add(Object obj)
}

这里的 <? super Integer> 被擦除为Object,因为 super Integer 表示可接收 Integer 、Number 、Object 等类型,只有 Object 可以覆盖全部类型

但编译器会在调用 add 方法时确保参数类型为 Integer 或其子类,不论你实现的时候指定的是 Number 还是 Object ,因为编译器不关心你实现时指定是具体类型,只确保写入元素的绝对安全性(真正起到减少ClassCastException异常的作用)

所以就算你实现时指定的是 Number 类型,也不能添加非 Integer 极其子类以外类型的数据(如 Doube),即使他们满足你指定的 Number 类型

桥接方法生成机制

当泛型接口被实现时,可能由于泛型擦除导致方法签名冲突:

//泛型擦除导致的方法签名冲突
interface Comparable<T> {int compareTo(T o);
}
class MyString implements Comparable<String> {    //这里的String编译后被擦除为Object类,而compareTo方法的String o参数不改变,导致签名冲突@Overridepublic int compareTo(String o) {  return 0;}
}

为了解决此冲突,编译器会在编译时自动生成桥接方法,将Object的o对象转换成符合方法参数的String类型:

//编译后代码
class MyString implements Comparable {// 用户实现的方法public int compareTo(String o) { return 0; }// 编译器生成的桥接方法public synthetic bridge int compareTo(Object o) {return compareTo((String) o);}
}

擦除机制导致的影响

泛型类型在运行时不能访问
//不能访问——例
List<String> stringList = new ArrayList<>();
List<Integer> intList = new ArrayList<>();System.out.println(stringList.getClass() == intList.getClass()); // true
// 编译是泛型类型都自动擦除为Object类,返回类型均为 ArrayList.class
无法创建泛型实例或数组

由于擦除后的泛型实例构造方法完全等价于Object类构造方法,明显不合预期,所以实例化泛型类型被禁止

由于擦除后的泛型数组类型信息丢失,可能导致加入类型冲突的安全问题,所以泛型数组的创建也被禁止

要获取实例化的泛型类型只能通过class<T>反射得到

//无法创建
public class Factory<T> {// 错误:无法创建泛型数组// private T[] array = new T[10];// 错误:无法实例化泛型类型// public T create() { return new T(); }// 正确做法:通过 Class<T> 实例化public T create(Class<T> clazz) throws Exception {return clazz.getDeclaredConstructor().newInstance();}
}

http://www.dtcms.com/a/294955.html

相关文章:

  • Excel导入SQLServer报错
  • 炬森精密:缓冲滑轨的创新力量,重塑家居静音与安全新体验
  • SSM框架中关于Spring MVC的技术问题
  • 智能迎宾的认知革命:Deepoc具身智能如何让机器人读懂人类微语言
  • 论文略读: Howto Merge Your Multimodal Models Over Time?
  • IGM弧焊机器人气体节约
  • SwiftUI 实战:构建一个复杂的图书首页长页面
  • 本地部署 Stable Diffusion:零基础搭建 AI文生图模型
  • Linux中scp命令传输文件到服务器报错
  • 直播软件搭建与原生直播系统开发全解析
  • 【2025目标检测】最新论文
  • VulhubDVWA靶场环境搭建及使用
  • 【Mysql】 Mysql zip解压版 Win11 安装备忘
  • Neo4j 框架 初步简单使用(基础增删改查)
  • OMS监考系统V2版本无法启动问题解决办法
  • [每日随题15] 前缀和 - 拓扑排序 - 树状数组
  • 海信IP501H-IP502h_GK6323处理器-原机安卓9专用-TTL线刷烧录可救砖
  • 【Java学习|黑马笔记|Day21】IO流|缓冲流,转换流,序列化流,反序列化流,打印流,解压缩流,常用工具包相关用法及练习
  • C++面试7——继承与多态
  • Xorg占用显卡内存问题和编译opencv GPU版本
  • InnoDB的redo log和 undo log
  • 智能小e-集成配置
  • Nestjs框架: 基于Prisma的多租户功能集成和优化
  • 使用抓取 API 可靠高效地提取亚马逊 (Amazon)数据
  • CCD工业相机系统设计——基于FPGA设计
  • SQL执行顺序
  • LLM 隐藏层特征增强技术
  • 同步型降压转换器的“同步”是什么意思?
  • Vite 7.0 引入的几个重要新 API 详解
  • 三极管与场效应管的对比