Skip to main content
在现代 Java 开发中,不可变性(Immutability)已成为构建健壮、线程安全应用的核心原则之一。本文将深入探讨如何使用 Java Records 和 Lombok 的 @Value 注解来优雅地实现不可变对象。

什么是不可变对象?

不可变对象是指一旦创建完成,其内部状态就无法被修改的对象。在 Java 中,不可变对象需要满足以下特征:
  • 状态固定:对象创建后,所有字段的值保持不变
  • 字段不可变:所有字段都使用 final 关键字修饰
  • 无修改方法:不提供任何 setter 方法或其他可以修改内部状态的方法
  • 防御性拷贝:如果包含可变对象的引用,不直接暴露这些引用

常见的不可变对象示例

Java 标准库中有许多经典的不可变类:

为什么不可变性如此重要?

1. 线程安全性

不可变对象天生就是线程安全的。由于状态永远不会改变,多个线程可以同时访问同一个对象而不需要任何同步机制。

2. 简化代码逻辑

不可变对象消除了意外状态变化的可能性,使代码更容易理解和维护。你不需要追踪对象在程序运行过程中的状态变化。

3. 适合作为 HashMap 的键

不可变对象的哈希码永远不会改变,这使它们非常适合作为 HashMapHashSet 的键。

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() 方法

@Value 的高级特性

使用 @With 创建修改副本

自定义构造函数

处理集合字段