@Value 注解来优雅地实现不可变对象。
什么是不可变对象?
不可变对象是指一旦创建完成,其内部状态就无法被修改的对象。在 Java 中,不可变对象需要满足以下特征:- 状态固定:对象创建后,所有字段的值保持不变
- 字段不可变:所有字段都使用
final关键字修饰 - 无修改方法:不提供任何 setter 方法或其他可以修改内部状态的方法
- 防御性拷贝:如果包含可变对象的引用,不直接暴露这些引用
常见的不可变对象示例
Java 标准库中有许多经典的不可变类:为什么不可变性如此重要?
1. 线程安全性
不可变对象天生就是线程安全的。由于状态永远不会改变,多个线程可以同时访问同一个对象而不需要任何同步机制。2. 简化代码逻辑
不可变对象消除了意外状态变化的可能性,使代码更容易理解和维护。你不需要追踪对象在程序运行过程中的状态变化。3. 适合作为 HashMap 的键
不可变对象的哈希码永远不会改变,这使它们非常适合作为HashMap 或 HashSet 的键。
4. 避免防御性拷贝
当方法返回不可变对象时,不需要创建防御性拷贝,因为调用者无法修改返回的对象。传统方式实现不可变类
在 Java 16 之前,我们需要手动编写大量样板代码来实现不可变类:- 代码冗长,样板代码过多
- 容易出错(比如忘记将类或字段声明为 final)
- 维护成本高,增加字段时需要修改多个方法
使用 Java Records 实现不可变性
Java 14 引入的 Records 为定义不可变数据类提供了一种简洁优雅的方式。基本用法
- 私有的 final 字段
- 公共的构造函数
- getter 方法(
name()和age()) equals()、hashCode()和toString()方法
Records 的特性
使用示例
处理可变字段
当 Record 包含可变对象时,需要特别注意:使用 Lombok @Value 实现不可变性
Lombok 是一个强大的 Java 库,通过注解自动生成样板代码。@Value 注解专门用于创建不可变类。
添加 Lombok 依赖
首先在项目中添加 Lombok 依赖:基本用法
@Value 注解会自动:
- 将类声明为
final - 将所有字段声明为
private final - 生成全参构造函数
- 生成 getter 方法(
getName()、getAge()) - 生成
equals()、hashCode()和toString()方法