Java深入解析篇三十三之密封类与接口(Sealed Classes)详解

Java 密封类与接口(Sealed Classes)详解

全面覆盖 Sealed Classes:从基础语法到代数数据类型建模,从模式匹配穷举到设计模式应用

适用版本:JDK 15(预览)→ JDK 17(正式)→ JDK 21+


一、密封类概述

1.1 什么是密封类

密封类(Sealed Classes)是 JDK 17 正式引入的语言特性,允许类或接口精确控制哪些类可以继承或实现它。

java 复制代码
// 传统继承:任何类都可以继承
public abstract class Shape { }
// 任何人都可以写:public class Pentagon extends Shape { }

// 密封继承:只有 permits 中的类可以继承
public sealed class Shape permits Circle, Rectangle, Triangle { }
// 编译错误:public class Pentagon extends Shape { }

1.2 解决的核心问题

问题 传统方式 密封类
类型层次不可控 任何类可继承 只有许可类可继承
模式匹配无法穷举 必须写 default 编译器验证穷举
建模意图不明确 靠文档约定 代码层面表达
ADT 无法表达 模拟实现 原生支持

1.3 版本演进

版本 状态
JDK 15 预览
JDK 16 第二次预览
JDK 17 正式标准化

二、基本语法

2.1 密封类

java 复制代码
// 密封类:只有 Circle、Rectangle、Triangle 可以继承
public sealed class Shape permits Circle, Rectangle, Triangle {
    public abstract double area();
}

2.2 密封接口

java 复制代码
// 密封接口:更常用(因为接口可以多实现)
public sealed interface PaymentResult permits PaymentSuccess, PaymentFailed, PaymentPending {
    String message();
}

2.3 permits 子句规则

java 复制代码
// 方式1:显式列出所有子类
sealed class Shape permits Circle, Rectangle, Triangle {}

// 方式2:省略 permits(子类必须在同一文件中)
// Shape.java 文件中:
sealed class Shape {}
final class Circle extends Shape {}     // OK,同一文件
final class Rectangle extends Shape {}  // OK,同一文件

// 方式3:子类在不同包中(需要在同一模块中)
// 模块 module.geometry 中:
sealed class Shape permits com.other.Circle {} // 需要同一模块

2.4 子类的三种选择

java 复制代码
sealed class Animal permits Dog, Cat, Bird {
    abstract void speak();
}

// 选择1:final --- 终止继承链
final class Dog extends Animal {
    @Override void speak() { System.out.println("Woof!"); }
}

// 选择2:sealed --- 继续密封
sealed class Cat extends Animal permits Lion, Tiger {
    @Override void speak() { System.out.println("Meow!"); }
}

// 选择3:non-sealed --- 开放继承
non-sealed class Bird extends Animal {
    @Override void speak() { System.out.println("Tweet!"); }
}

// Bird 是 non-sealed,任何类都可以继承
class Sparrow extends Bird {} // OK
class Eagle extends Bird {}   // OK

// Cat 是 sealed,只有 Lion 和 Tiger 可以继承
final class Lion extends Cat {
    @Override void speak() { System.out.println("Roar!"); }
}
final class Tiger extends Cat {
    @Override void speak() { System.out.println("Grrr!"); }
}

三、密封类与模式匹配

3.1 switch 穷举检查

java 复制代码
sealed interface Shape permits Circle, Rectangle, Triangle {
    double area();
}

record Circle(double radius) implements Shape {
    @Override public double area() { return Math.PI * radius * radius; }
}

record Rectangle(double width, double height) implements Shape {
    @Override public double area() { return width * height; }
}

record Triangle(double a, double b, double c) implements Shape {
    @Override
    public double area() {
        double s = (a + b + c) / 2;
        return Math.sqrt(s * (s - a) * (s - b) * (s - c));
    }
}

// 编译器穷举检查:不需要 default!
// 如果新增一个 Square implements Shape,编译器会在这里报错
static String describe(Shape shape) {
    return switch (shape) {
        case Circle c    -> "圆形,半径: " + c.radius();
        case Rectangle r -> "矩形,宽: " + r.width() + ",高: " + r.height();
        case Triangle t  -> "三角形,边: " + t.a() + ", " + t.b() + ", " + t.c();
        // 没有 default --- 编译器知道已覆盖所有可能
    };
}

3.2 Record Patterns 解构

java 复制代码
// JDK 21:Record Patterns 与密封类组合
sealed interface Expr permits Num, Add, Mul {}
record Num(double value) implements Expr {}
record Add(Expr left, Expr right) implements Expr {}
record Mul(Expr left, Expr right) implements Expr {}

// 解构 + 穷举
String prettyPrint(Expr expr) {
    return switch (expr) {
        case Num(var v) -> String.valueOf(v);
        case Add(var l, var r) -> "(" + prettyPrint(l) + " + " + prettyPrint(r) + ")";
        case Mul(var l, var r) -> "(" + prettyPrint(l) + " * " + prettyPrint(r) + ")";
    };
}

// (2 + 3) * 4
Expr example = new Mul(new Add(new Num(2), new Num(3)), new Num(4));
System.out.println(prettyPrint(example)); // ((2.0 + 3.0) * 4.0)

四、代数数据类型(ADT)建模

4.1 JSON 值类型

java 复制代码
sealed interface JsonValue
    permits JsonString, JsonNumber, JsonBoolean, JsonNull, JsonArray, JsonObject {}

record JsonString(String value) implements JsonValue {}
record JsonNumber(double value) implements JsonValue {}
record JsonBoolean(boolean value) implements JsonValue {}
record JsonNull() implements JsonValue {}
record JsonArray(List<JsonValue> elements) implements JsonValue {}
record JsonObject(Map<String, JsonValue> properties) implements JsonValue {}

// 类型安全的 JSON 渲染
String render(JsonValue value) {
    return switch (value) {
        case JsonString(var s) -> "\"" + s + "\"";
        case JsonNumber(var n) -> String.valueOf(n);
        case JsonBoolean(var b) -> String.valueOf(b);
        case JsonNull() -> "null";
        case JsonArray(var elems) ->
            "[" + elems.stream().map(this::render).collect(Collectors.joining(", ")) + "]";
        case JsonObject(var props) ->
            "{" + props.entrySet().stream()
                .map(e -> "\"" + e.getKey() + "\": " + render(e.getValue()))
                .collect(Collectors.joining(", ")) + "}";
    };
}

4.2 状态机

java 复制代码
// 订单状态机
sealed interface OrderState permits Created, Paid, Shipped, Delivered, Cancelled {
    String description();
}

record Created() implements OrderState {
    @Override public String description() { return "订单已创建"; }
}
record Paid(String paymentId) implements OrderState {
    @Override public String description() { return "已支付: " + paymentId; }
}
record Shipped(String trackingNo) implements OrderState {
    @Override public String description() { return "已发货: " + trackingNo; }
}
record Delivered(LocalDateTime time) implements OrderState {
    @Override public String description() { return "已送达: " + time; }
}
record Cancelled(String reason) implements OrderState {
    @Override public String description() { return "已取消: " + reason; }
}

// 状态转换验证
OrderState next(OrderState current, OrderAction action) {
    return switch (current) {
        case Created c -> switch (action) {
            case Pay p -> new Paid(p.paymentId());
            case Cancel x -> new Cancelled(x.reason());
            default -> throw new IllegalStateException("非法状态转换");
        };
        case Paid p -> switch (action) {
            case Ship s -> new Shipped(s.trackingNo());
            default -> throw new IllegalStateException("非法状态转换");
        };
        // ... 其他状态
        default -> throw new IllegalStateException("终态不可转换");
    };
}

4.3 结果类型(Either)

java 复制代码
// 函数式结果类型
sealed interface Result<T> permits Success, Failure {}
record Success<T>(T value) implements Result<T> {}
record Failure<T>(Exception error) implements Result<T> {}

// 使用
<T> Result<T> tryExecute(Supplier<T> supplier) {
    try {
        return new Success<>(supplier.get());
    } catch (Exception e) {
        return new Failure<>(e);
    }
}

// 处理结果
<T, R> R handle(Result<T> result, Function<T, R> onSuccess, Function<Exception, R> onFailure) {
    return switch (result) {
        case Success<T>(var value) -> onSuccess.apply(value);
        case Failure<T>(var error) -> onFailure.apply(error);
    };
}

// 实际使用
Result<Integer> result = tryExecute(() -> 42 / 0);
String message = handle(result,
    v -> "成功: " + v,
    e -> "失败: " + e.getMessage()
);

4.4 命令模式

java 复制代码
sealed interface Command permits StartCmd, StopCmd, StatusCmd, RestartCmd {
    String serviceName();
}

record StartCmd(String serviceName) implements Command {}
record StopCmd(String serviceName) implements Command {}
record StatusCmd(String serviceName) implements Command {}
record RestartCmd(String serviceName, boolean force) implements Command {}

// 命令处理器
String execute(Command cmd) {
    return switch (cmd) {
        case StartCmd(var name) -> "启动服务: " + name;
        case StopCmd(var name) -> "停止服务: " + name;
        case StatusCmd(var name) -> "查询状态: " + name;
        case RestartCmd(var name, var force) ->
            "重启服务: " + name + (force ? "(强制)" : "");
    };
}

五、密封类与反射

java 复制代码
// 获取密封类信息
Class<?> clazz = Shape.class;

// 检查是否是密封类
System.out.println(clazz.isSealed()); // true

// 获取允许的子类
Class<?>[] permitted = clazz.getPermittedSubclasses();
for (Class<?> c : permitted) {
    System.out.println(c.getName());
    // Circle, Rectangle, Triangle
}

六、最佳实践

6.1 何时使用密封类

场景 是否适合
有限的类型集合(状态、结果) ✅ 非常适合
代数数据类型建模 ✅ 非常适合
模式匹配穷举 ✅ 非常适合
需要扩展的框架接口 ❌ 不适合
第三方库需要实现的接口 ❌ 不适合
类型数量不确定 ❌ 不适合

6.2 设计原则

java 复制代码
// 原则1:密封类搭配不可变实现(Record)
sealed interface Event permits UserCreated, UserDeleted, UserUpdated {}
record UserCreated(String id, String name) implements Event {}
record UserDeleted(String id) implements Event {}
record UserUpdated(String id, Map<String, Object> changes) implements Event {}

// 原则2:密封接口优于密封类(更灵活)
// 接口可以多实现,类只能单继承

// 原则3:配合 switch 表达式使用
// 避免 if-else 链

// 原则4:在模块边界内使用
// 密封类最适合在模块内部定义类型层次

总结:密封类与 Record、Pattern Matching 共同构成现代 Java 类型系统的三件套。密封类控制"谁可以实现",Record 定义"数据长什么样",模式匹配处理"如何分支"。三者组合使 Java 具备了类似函数式语言的代数数据类型建模能力。

相关推荐
nagualky123几秒前
GPT-4 系列退役在即:一套可回滚的模型迁移验收法
java·开发语言·ai·架构
今天的砖头有点烫手啊1 分钟前
Java 8 Stream 原理拆解:惰性求值与并行流陷阱
java·jvm
Ai-_Man3 分钟前
DeepSeek生成的内容能否下载吗?
开发语言·javascript·人工智能·小程序·ecmascript
Evelvnk4 分钟前
计算机学习资料
开发语言·人工智能·计算机·php
starzy19908 分钟前
Flink 任务调度 Graph 源码深度剖析:从 StreamGraph 到 ExecutionGraph 的三层演进
java·大数据·flink
Eyfcom10 分钟前
〖实战〗快运系统 Oracle 性能优化:从“能用”到“扛住双11”的 Java + Spring Boot 实战
java·oracle·性能优化·快递管理系统
geovindu15 分钟前
rust: Chain of Responsibility Pattern
开发语言·后端·设计模式·rust·责任链模式·行为模式
m0_5873830015 分钟前
碰碰好评卡功能落地全流程实战指南 附核心逻辑与常见问题解决方案
java·前端·数据库·后端·struts·需求分析
cheems952715 分钟前
从统一响应到 Swagger:一篇讲清微服务接口文档设计未命名
java·微服务·架构·状态模式
阿里嘎多学长18 分钟前
2026-10-14 GitHub 热点项目精选
开发语言·程序员·github·代码托管