欢迎光临
我们一直在努力

record + sealed class = Java 的“类型乐高”!

📌 JDK 版本要求:本文代码基于 JDK 17+ 编写并验证。

  • record 自 JDK 14 起预览,JDK 16 正式发布;

  • sealed class 自 JDK 15 起预览,JDK 17(LTS)正式发布;

  • switch 对 sealed class 的穷尽性检查需 JDK 17+。

    建议使用 JDK 17 或更高 LTS 版本(如 JDK 21) 运行示例。

今天来学习用 record 和 sealed class 实现 代数数据类型(ADT),写出编译器帮你兜底的模式匹配代码!发车!

一、场景引入:从“满屏 if-else”到“类型即文档”

你是不是也写过这样的状态机?

public void handleEvent(String eventType, Object data) {
if ("LOGIN".equals(eventType)) {
// 处理登录
} else if ("LOGOUT".equals(eventType)) {
// 处理登出
} else if ("PAYMENT".equals(eventType)) {
// 处理支付
} else {
throw new IllegalArgumentException("未知事件: " + eventType);
}
}

这段代码的问题:

  • 字符串魔法值,易拼错
  • 类型不安全(Object data)
  • 新增事件时,容易漏改处理逻辑

运行效果:看似能跑,但一旦事件类型增加,维护成本指数级上升!

二、核心武器一:record —— 不可变数据的终极语法糖

record 是一种紧凑型类声明,专为“纯数据载体”设计。

示例:定义一个点

public record Point(int x, int y) {}

等价于手写 50 行代码,但只需一行!

使用:

Point p = new Point(3, 4);
System.out.println(p.x()); // → 输出:3
System.out.println(p); // → 输出:Point[x=3, y=4]
System.out.println(p.equals(new Point(3, 4))); // → 输出:true

自动生成:构造器、accessor、equals/hashCode/toString,且字段不可变!

三、核心武器二:sealed class —— 封闭继承,掌控子类

sealed class 允许你精确控制哪些类可以继承它!

定义封闭的“形状”体系:

public sealed interface Shape permits Circle, Rectangle, Triangle {}

具体实现(必须是 final、sealed 或 non-sealed):

public record Circle(double radius) implements Shape {}
public record Rectangle(double width, double height) implements Shape {}
public record Triangle(double a, double b, double c) implements Shape {}

关键特性:编译器知道 Shape 只有这三种子类型,为模式匹配提供基础。

四、终极组合:switch + sealed class = 穷尽性检查!

从 JDK 17+ 开始,switch 支持对 sealed class 进行穷尽性检查!

计算面积:

public static double area(Shape shape) {
return switch (shape) {
case Circle c -> Math.PI * c.radius() * c.radius();
case Rectangle r -> r.width() * r.height();
case Triangle t -> {
double s = (t.a() + t.b() + t.c()) / 2;
yield Math.sqrt(s * (s t.a()) * (s t.b()) * (s t.c()));
}
}; // 注意:没有 default!
}

调用与输出:

Shape circle = new Circle(2.0);
Shape rect = new Rectangle(3.0, 4.0);
Shape tri = new Triangle(3.0, 4.0, 5.0);

System.out.printf("圆面积: %.2f%n", area(circle)); // → 输出:圆面积: 12.57
System.out.printf("矩形面积: %.2f%n", area(rect)); // → 输出:矩形面积: 12.00
System.out.printf("三角形面积: %.2f%n", area(tri)); // → 输出:三角形面积: 6.00

如果漏掉某个 case,编译直接报错!

新增子类(如 Square),所有 switch 必须更新,否则无法编译!

五、实战演示:构建“订单状态机”

1. 定义封闭状态类型

public sealed interface OrderStatus
permits Pending, Confirmed, Shipped, Cancelled {}

public record Pending() implements OrderStatus {}
public record Confirmed(String confirmTime) implements OrderStatus {}
public record Shipped(String trackingNumber) implements OrderStatus {}
public record Cancelled(String reason) implements OrderStatus {}

2. 安全处理状态

public static String getStatusMessage(OrderStatus status) {
return switch (status) {
case Pending p -> "订单待确认";
case Confirmed c -> "已确认,时间: " + c.confirmTime();
case Shipped s -> "已发货,单号: " + s.trackingNumber();
case Cancelled c -> "已取消,原因: " + c.reason();
};
}

调用与输出:

OrderStatus[] statuses = {
new Pending(),
new Confirmed("2026-02-10 10:00"),
new Shipped("SF123456789CN"),
new Cancelled("用户取消")
};

for (OrderStatus status : statuses) {
System.out.println(getStatusMessage(status));
}

输出:

订单待确认
已确认,时间: 2026-02-10 10:00
已发货,单号: SF123456789CN
已取消,原因: 用户取消

零字符串魔法值!零 NPE!零遗漏!

六、最佳实践:5 条黄金法则

  • 用 record 表示纯数据

    如 DTO、配置项、事件载荷、几何对象等。

  • 用 sealed class 表示有限状态/变体

    如状态机、AST 节点、协议消息类型。

  • 配合 switch 表达式实现穷尽处理

    利用编译器检查,杜绝逻辑遗漏。

  • 不要给 record 加业务方法

    它是值对象,不是行为载体。复杂逻辑放在服务类中。

  • 在 API 边界使用,提升契约清晰度

    比如返回 Result<T>(Success/Failure),比抛异常更函数式。

  • 七、注意事项

    问题解决方案
    “record 能继承吗?” ❌ 不能!它是 final 的
    “sealed class 能跨模块吗?” ✅ 可以!用 permits 显式声明,或同一模块内自动允许
    “旧代码怎么兼容?” ✅ record 本质仍是普通类,字节码兼容

    八、代码在哪?

    本篇涉及到的代码已上传至 GitHub:

    https://github.com/iweidujiang/java-tricks-lab/tree/main/05-record-sealed-class

    欢迎 star & fork,下期还有神操作!

    赞(0)
    未经允许不得转载:171主机测评 » record + sealed class = Java 的“类型乐高”!
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址