泛型知识理解

最近用Java刷算法题的过程中, 很多地方用到了泛型的知识. 快忘完了正好借此机会重新熟悉一下, 在脑子里面建立对泛型的理解.

一、泛型的编译期需要知道的地方

1.1 泛型类型约束机制

泛型的本质是编译器对类型的约束,通过在声明时指定具体类型,编译器会强制检查所有操作是否符合该类型约束。

示例代码:

java 复制代码
// 声明泛型类型为Integer
List<Integer> list = new ArrayList<>();

// 编译器会检查add方法的参数类型
list.add(100);          // 合法:参数为Integer类型
list.add("hello");      // 编译错误:参数必须为Integer类型

// 编译器自动插入类型转换代码
Integer value = list.get(0);  // 等价于 (Integer)list.get(0)

1.2 类型擦除后的底层实现

带泛型的对象在实际创建时所有泛型参数都会被替换为Object或上界类型, 如下所示:

java 复制代码
// 我们编写完的泛型类长这样
public class Box<T> {
    private T value;
    public T get() { return value; }
    public void set(T value) { this.value = value; }
}

// 实际创建对象时, 对象在内存中实际长这样
public class Box {
    private Object value;
    public Object get() { return value; }
    public void set(Object value) { this.value = value; }
}

1.3 菱形语法的类型推断

Java 7引入的菱形语法(<>)允许省略右侧泛型声明,编译器会根据左侧类型自动推断。

示例代码:

java 复制代码
// 完整写法(Java 6及以前)
List<String> list1 = new ArrayList<String>();

// 简化写法(Java 7+)
List<String> list2 = new ArrayList<>();  // 自动推断为String类型

// 复杂类型推断示例
Map<Integer, List<String>> map = new HashMap<>();
map.put(1, List.of("a", "b"));

二、JVM层面对理解泛型

2.1 引用类型内存一致性

所有引用变量在内存中占用相同大小的存储空间(64位JVM通常为8字节),因此JVM在内存中创建的对象虽然都一样, 但是给泛型赋上不同类型后还是能正常存储就是这个原因.

内存分配示意图:

java 复制代码
// 泛型类定义
class Pair<T, U> {
    private T first;   // 引用类型,占8字节
    private U second;  // 引用类型,占8字节
}

// 实例化不同类型的Pair
Pair<String, Integer> p1 = new Pair<>();
Pair<Double, List> p2 = new Pair<>();

// 内存布局(简化)
// p1: [first引用(8B) | second引用(8B)]
// p2: [first引用(8B) | second引用(8B)]

2.2 这也解释了为什么泛型T不能是基本类型

泛型参数不能是基本类型,因为基本类型的存储大小不一致(如int占4字节,long占8字节),无法用统一的内存布局处理, 如下假如可以放入int类型的值, JVM在内存中创建的是8字节的数组, 那放进去就会出问题.

错误示例:

java 复制代码
List<int> errorList = new ArrayList<>();  // 编译错误:不能使用基本类型

// 必须使用包装类
List<Integer> correctList = new ArrayList<>();

2.3 自动装箱与拆箱

使用包装类时,编译器会自动处理基本类型与包装类型之间的转换。

示例代码:

java 复制代码
List<Integer> list = new ArrayList<>();
list.add(10);  // 自动装箱:int → Integer
int value = list.get(0);  // 自动拆箱:Integer → int

// 等价的手动写法
list.add(Integer.valueOf(10));
int value = list.get(0).intValue();

三、总结

等号右边创建的均是一个T->object的对象, 我们用的时候只要关注右边是什么对象就行, 不用考虑那么多;

等号左边声明的是一个T->特定类型的引用变量, 编译器在编译时起到了保安的作用, 会检查你输入的元素是否是这个特定类型.

所以见到泛型和用泛型的时候, 例如:

Java 复制代码
List<Integer> list = new ArrayList<>();

我们的理解应该是, 左边是一个List类型的引用变量, 右边是ArrayList类型的对象, 大类的引用变量接小类的对象. 此外现在还加上了<Integer> , 代表这个List引用只能加入Integer的类型的元素.

相关推荐
晨曦中的暮雨6 小时前
限流算法——固定窗口、滑动窗口、漏桶、令牌桶
java·开发语言·算法
文丑颜不良啊7 小时前
LeetCode876~900题目解析
java·leetcode
aloha_7898 小时前
Java 基础、集合、异常与 JUnit 5 速查
java·junit·sqlserver
知守观9 小时前
设计模式实战:从真实老项目中看标准模板方法与工厂的落地
java·spring·设计模式
独隅9 小时前
Claude API 实战教程:从入门到完整项目
android·java·数据库·claude·claude code
Wang's Blog9 小时前
Java 项目实战: 交友项目 - 圈子功能实现:总结
java·开发语言·交友
SL_staff9 小时前
JVS私有化文档系统上线避坑指南:5大运维与业务协同断点及检测加固实践
java·开源·全栈
FinelyYang11 小时前
考试系统简答题 AI 阅卷:覆盖度驱动、异步评分与人机协同落地实践
java·大数据·人工智能
hhhjhl11 小时前
Yup 跨端三方库仓颉适配全流程
java·开发语言
用户31268748772011 小时前
Java 对象内存布局:压缩指针、对齐填充与 JOL 实测
java