[toc]
84. Java Record 类型的使用与解析
1. 引言
在 Java 中,我们经常需要创建不可变的数据模型(immutable data model)。过去,最常见的方式是使用 final 关键字修饰类和字段,并提供构造方法进行初始化。这种方式虽然可靠,但需要手动编写大量重复的代码,例如 toString()、equals() 和 hashCode() 方法,甚至还可能需要实现 Serializable 接口来支持序列化。这种方式不仅繁琐,而且容易出错。
从 Java 14 开始,JDK 引入了 record 关键字,使得创建不可变类变得更加简单高效。接下来,我们将详细探讨 record 的用法,并通过示例帮助大家更好地理解。
2. 传统方式创建不可变类
示例:使用普通类创建 Point
public final class Point {
private final int x;
private final int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
@Override
public String toString() {
return "Point{" + "x=" + x + ", y=" + y + '}';
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Point point = (Point) obj;
return x == point.x && y == point.y;
}
@Override
public int hashCode() {
return Objects.hash(x, y);
}
}
存在的问题
- 需要手动编写构造方法、访问器(
getter)、toString()、equals()和hashCode()方法。 - 如果修改类的字段,如增加
z轴,需要手动更新equals()和hashCode(),容易出错。 - 代码冗长,影响可读性和维护性。
3. 使用 Record 简化代码
在 Java 14 及以上版本,我们可以使用 record 关键字大幅简化上述代码。
示例:使用 Record 创建 Point
public record Point(int x, int y) {}
这行代码就完成了所有必要的工作!
Record 生成的内容
Point类是final的,不能被继承。x和y作为private final字段存在。- 自动生成
x()和y()方法,代替getX()和getY()。 - 自动生成
toString()、equals()和hashCode()方法。 record还可以自动支持Serializable。
示例:调用 Record 方法
public class Main {
public static void main(String[] args) {
Point p1 = new Point(3, 5);
System.out.println(p1); // 输出:Point[x=3, y=5]
Point p2 = new Point(3, 5);
System.out.println(p1.equals(p2)); // 输出:true
System.out.println(p1.hashCode()); // 自动生成哈希码
}
}
4. Record 的构造器
4.1 默认构造器
record 默认提供一个全参数构造器:
public record Point(int x, int y) {}
// 等价于:
public Point(int x, int y) {
this.x = x;
this.y = y;
}
4.2 自定义构造器(紧凑构造器)
如果需要对参数进行校验,可以使用紧凑构造器(compact constructor):
public record Range(int start, int end) {
public Range {
if (end <= start) {
throw new IllegalArgumentException("end 必须大于 start");
}
}
}
示例:
Range r1 = new Range(5, 10); // 正常创建
Range r2 = new Range(10, 5); // 抛出异常
4.3 标准构造器
如果不想使用紧凑构造器,也可以显式定义构造方法:
public record Range(int start, int end) {
public Range(int start, int end) {
if (end <= start) {
throw new IllegalArgumentException("end 必须大于 start");
}
this.start = start;
this.end = end;
}
}
5. Record 的限制
-
不能添加额外的实例字段:
- 例如,以下代码会报错:
public record Point(int x, int y) { private int z; // ❌ 错误,record 不能有额外的实例字段 } -
不能定义字段初始值:
- 例如,以下代码是非法的:
public record Point(int x, int y) { private final int z = 10; // ❌ 不能手动初始化字段 } -
不能有实例初始化块:
- 例如,以下代码是非法的:
public record Point(int x, int y) { { System.out.println("初始化"); } // ❌ record 不能有实例初始化块 }
- 可以定义静态字段和静态初始化块。
public record Config(String name) {
static String DEFAULT_NAME = "Unknown";
static {
System.out.println("Config 类已加载");
}
}
🎯 record 适用场景
- 数据传输对象(
DTO)。 - 不可变数据建模(如
Point、Color)。 - 简单的键值映射。
6. 总结
record是Java 14引入的新特性,用于简化不可变类的创建。record自动生成构造器、访问器、toString()、equals()和hashCode()。record是final的,不能被继承,但可以实现接口。- 可以使用紧凑构造器进行参数校验。
record不能有额外实例字段,但可以有静态字段。