Java 17 引入了 Records(记录类) ,这是 Java 语言中一种新的类类型,用于简化数据载体类的定义。Records 是不可变的数据类,自动提供了 equals()
、hashCode()
、toString()
等方法,从而减少了样板代码的编写。
这一特性最初在 Java 14 中作为预览功能引入,并在 Java 16 中成为正式特性。Java 17 继续支持并优化了这一功能。
为什么需要 Records?
在传统的 Java 开发中,当我们需要创建一个简单的数据载体类时,通常需要编写大量的样板代码,例如:
- 构造函数
equals()
方法hashCode()
方法toString()
方法- Getter 方法
这些代码虽然可以通过 IDE 自动生成,但仍然会增加代码的复杂性,降低可读性。
Records 的目标是通过简化的语法,自动生成这些方法,使代码更加简洁和直观。
Records 的基本用法
定义一个 Record 类
使用 record
关键字定义一个记录类:
arduino
record Person(String name, int age) {}
示例代码:
csharp
public class Main {
public static void main(String[] args) {
Person person = new Person("Alice", 25);
System.out.println(person); // 自动调用 toString()
System.out.println("Name: " + person.name());
System.out.println("Age: " + person.age());
}
}
输出:
makefile
Person[name=Alice, age=25]
Name: Alice
Age: 25
Records 的特点
- 自动生成构造函数
- 编译器会自动为 Record 类生成一个构造函数,包含所有字段。
- 你可以根据需要自定义构造函数。
arduino
record Person(String name, int age) {
// 自定义构造函数
public Person {
if (age < 0) {
throw new IllegalArgumentException("Age cannot be negative");
}
}
}
- 自动生成访问器方法
- 每个字段都会自动生成一个对应的访问器方法(getter),方法名与字段名相同。
- 不需要显式地编写
getName()
或getAge()
。
ini
Person person = new Person("Alice", 25);
String name = person.name(); // 访问 name 字段
int age = person.age(); // 访问 age 字段
- 自动生成
equals()
和hashCode()
- Records 会根据字段值自动生成
equals()
和hashCode()
方法。 - 两个 Record 对象相等的条件是它们的所有字段值都相等。
ini
Person p1 = new Person("Alice", 25);
Person p2 = new Person("Alice", 25);
System.out.println(p1.equals(p2)); // true
- 自动生成
toString()
- 编译器会自动生成
toString()
方法,格式为:RecordName[field1=value1, field2=value2]
。
ini
Person person = new Person("Alice", 25);
System.out.println(person); // 输出:Person[name=Alice, age=25]
- 不可变性
- Record 类的字段是隐式
final
的,无法修改。 - 这使得 Record 类非常适合用作不可变数据对象。
ini
Person person = new Person("Alice", 25);
// person.name("Bob"); // 编译错误:无法修改字段
Records 的限制
- 不能继承其他类
- Record 类隐式继承自
java.lang.Record
,因此不能显式继承其他类。
dart
record Person(String name, int age) extends Object {} // 编译错误
- 不能声明非静态字段
- Record 类只能包含声明的组件字段,不能添加额外的实例字段。
arduino
record Person(String name, int age) {
private int id; // 编译错误:不能添加额外的实例字段
}
- 不能定义额外的构造函数
- 只能定义紧凑构造函数(Compact Constructor)或重载构造函数。
arduino
record Person(String name, int age) {
public Person() { // 编译错误:不能定义无参构造函数
this("", 0);
}
}
Records 的高级用法
1. 局部类实现接口
Record 类可以实现接口,但不能继承其他类。
java
interface Greeting {
void greet();
}
record Person(String name, int age) implements Greeting {
@Override
public void greet() {
System.out.println("Hello, my name is " + name);
}
}
Person person = new Person("Alice", 25);
person.greet(); // 输出:Hello, my name is Alice
2. 嵌套 Record
可以在其他类中定义嵌套的 Record 类。
arduino
class OuterClass {
record Point(int x, int y) {}
}
OuterClass.Point point = new OuterClass.Point(10, 20);
System.out.println(point); // 输出:Point[x=10, y=20]
3. 泛型 Record
Record 类支持泛型。
sql
record Pair<T, U>(T first, U second) {}
Pair<String, Integer> pair = new Pair<>("Alice", 25);
System.out.println(pair); // 输出:Pair[first=Alice, second=25]
适用场景
- 数据传输对象(DTO) :
- 在微服务或 API 调用中,常用于封装请求和响应数据。
- 不可变对象:
- 当需要创建不可变的对象时,Record 类是一个很好的选择。
- 简单实体类:
- 在领域驱动设计(DDD)中,可以用 Record 类表示值对象(Value Object)。
- 减少样板代码:
- 替代传统的 POJO 类,减少冗余代码。
总结
Records 是 Java 17 中一项非常实用的新特性,特别适合用来定义简单的数据载体类。它通过自动生成构造函数、访问器方法、equals()
、hashCode()
和 toString()
等方法,极大地简化了代码的编写,同时保证了不可变性和线程安全性。