【Java SE】sealed关键字

sealed关键字

在Java 15(预览)/ Java 17(正式)中,sealed关键字的引入为Java的类型系统带来了革命性的变化。它允许开发者精确控制哪些类可以继承或实现某个类型,在开放性和封闭性之间找到了完美的平衡点。

为什么需要sealed?

传统的Java类继承机制存在一个两难困境:

  • 使用final:完全禁止继承,过于严格
  • 不添加限制:任何人都可以继承,可能导致滥用
java 复制代码
// 传统方案的问题
public class Animal { }  // 任何人都可以继承

public final class Cat extends Animal { }  // 完全禁止继承

在实际开发中,我们常常需要一种受控的继承 :明确知道哪些子类存在,同时允许这些子类自由扩展。这正是sealed关键字要解决的问题。

sealed类基础语法

基本声明

使用sealed关键字声明一个类或接口,并通过permits子句列出允许的子类型:

java 复制代码
// 定义sealed类,只允许Dog和Cat继承
public sealed class Animal permits Dog, Cat {
    protected String name;
    
    public Animal(String name) {
        this.name = name;
    }
    
    public abstract String makeSound();
}

// 子类可以是final
final class Dog extends Animal {
    public Dog(String name) {
        super(name);
    }
    
    @Override
    public String makeSound() {
        return "Woof!";
    }
}

// 子类可以是非sealed(允许进一步继承)
non-sealed class Cat extends Animal {
    public Cat(String name) {
        super(name);
    }
    
    @Override
    public String makeSound() {
        return "Meow!";
    }
}

// 子类也可以是sealed
sealed class Bird extends Animal permits Sparrow, Eagle {
    public Bird(String name) {
        super(name);
    }
}

子类的三种修饰符⭐

sealed类的子类必须声明以下三种修饰符之一:

修饰符 含义 适用场景
final 禁止进一步继承 叶子节点类
sealed 继续受限继承 需要进一步细分的中间类
non-sealed 恢复开放性 允许任意扩展
java 复制代码
// 完整的继承层次示例
public sealed class Shape permits Circle, Rectangle, Triangle {
    public abstract double area();
}

// final:不能再被继承
final class Circle extends Shape {
    private double radius;
    
    public Circle(double radius) {
        this.radius = radius;
    }
    
    @Override
    public double area() {
        return Math.PI * radius * radius;
    }
}

// sealed:继续受限继承
sealed class Rectangle extends Shape permits Square {
    protected double width;
    protected double height;
    
    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }
    
    @Override
    public double area() {
        return width * height;
    }
}

final class Square extends Rectangle {
    public Square(double side) {
        super(side, side);
    }
}

// non-sealed:恢复开放性
non-sealed class Triangle extends Shape {
    private double base;
    private double height;
    
    public Triangle(double base, double height) {
        this.base = base;
        this.height = height;
    }
    
    @Override
    public double area() {
        return 0.5 * base * height;
    }
}

// Triangle是non-sealed,所以可以任意继承
class RightTriangle extends Triangle {
    public RightTriangle(double base, double height) {
        super(base, height);
    }
}

sealed接口

sealed同样适用于接口,这在定义领域模型时尤为有用:

java 复制代码
// 定义sealed接口
public sealed interface PaymentMethod 
    permits CreditCard, PayPal, WeChatPay {
    
    void pay(BigDecimal amount);
    String getTransactionId();
}

// 实现类可以是record(record默认final)
record CreditCard(String cardNumber, String cvv) 
    implements PaymentMethod {
    
    @Override
    public void pay(BigDecimal amount) {
        System.out.println("支付 " + amount + " 元,使用信用卡: " + cardNumber);
    }
    
    @Override
    public String getTransactionId() {
        return "CC_" + UUID.randomUUID();
    }
}

record PayPal(String email) implements PaymentMethod {
    @Override
    public void pay(BigDecimal amount) {
        System.out.println("支付 " + amount + " 元,使用PayPal: " + email);
    }
    
    @Override
    public String getTransactionId() {
        return "PP_" + UUID.randomUUID();
    }
}

non-sealed class WeChatPay implements PaymentMethod {
    private String openId;
    
    public WeChatPay(String openId) {
        this.openId = openId;
    }
    
    @Override
    public void pay(BigDecimal amount) {
        System.out.println("支付 " + amount + " 元,使用微信支付: " + openId);
    }
    
    @Override
    public String getTransactionId() {
        return "WX_" + System.currentTimeMillis();
    }
}

与模式匹配的完美配合

sealed类与Java的模式匹配(Pattern Matching)是天作之合。由于编译器知道所有可能的子类型,可以实现穷举性检查

java 复制代码
public class PaymentProcessor {
    
    // 使用模式匹配处理支付
    public static String processPayment(PaymentMethod payment) {
        // 编译器可以验证所有情况都已覆盖
        return switch (payment) {
            case CreditCard cc -> 
                String.format("处理信用卡支付: %s", cc.cardNumber());
            case PayPal pp -> 
                String.format("处理PayPal支付: %s", pp.email());
            case WeChatPay wp -> 
                String.format("处理微信支付: %s", "交易完成");
        };
    }
    
    // 处理sealed类的继承层次
    public static double calculateArea(Shape shape) {
        return switch (shape) {
            case Circle c -> Math.PI * c.radius() * c.radius();
            case Rectangle r -> r.width() * r.height();
            case Square s -> s.side() * s.side();
            case Triangle t -> 0.5 * t.base() * t.height();
        };
    }
}

实际应用场景

领域驱动设计(DDD)中的值对象

java 复制代码
// 定义货币类型,只允许特定的币种
public sealed interface Currency permits USD, EUR, CNY, JPY {
    String getCode();
    String getSymbol();
}

record USD() implements Currency {
    @Override
    public String getCode() { return "USD"; }
    @Override
    public String getSymbol() { return "$"; }
}

record EUR() implements Currency {
    @Override
    public String getCode() { return "EUR"; }
    @Override
    public String getSymbol() { return "€"; }
}

record CNY() implements Currency {
    @Override
    public String getCode() { return "CNY"; }
    @Override
    public String getSymbol() { return "¥"; }
}

record JPY() implements Currency {
    @Override
    public String getCode() { return "JPY"; }
    @Override
    public String getSymbol() { return "¥"; }
}

表达式求值器

java 复制代码
// 定义表达式类型,只允许三种表达式
public sealed interface Expression 
    permits Constant, Addition, Multiplication {
    
    double evaluate();
}

record Constant(double value) implements Expression {
    @Override
    public double evaluate() {
        return value;
    }
}

record Addition(Expression left, Expression right) implements Expression {
    @Override
    public double evaluate() {
        return left.evaluate() + right.evaluate();
    }
}

record Multiplication(Expression left, Expression right) implements Expression {
    @Override
    public double evaluate() {
        return left.evaluate() * right.evaluate();
    }
}

// 使用示例
public class Calculator {
    public static void main(String[] args) {
        // 表达式: (3 + 4) * 5
        Expression expr = new Multiplication(
            new Addition(new Constant(3), new Constant(4)),
            new Constant(5)
        );
        
        System.out.println("计算结果: " + expr.evaluate()); // 35.0
    }
}

JSON值类型建模

java 复制代码
// 定义JSON值的所有可能类型
public sealed interface JsonValue 
    permits JsonNull, JsonBoolean, JsonNumber, JsonString, JsonArray, JsonObject {
}

record JsonNull() implements JsonValue {}

record JsonBoolean(boolean value) implements JsonValue {}

record JsonNumber(double value) implements JsonValue {}

record JsonString(String value) implements JsonValue {}

record JsonArray(List<JsonValue> values) implements JsonValue {}

record JsonObject(Map<String, JsonValue> properties) implements JsonValue {}

// 类型安全的JSON解析器
public class JsonParser {
    public static String toJsonString(JsonValue value) {
        return switch (value) {
            case JsonNull() -> "null";
            case JsonBoolean(var b) -> Boolean.toString(b);
            case JsonNumber(var n) -> Double.toString(n);
            case JsonString(var s) -> "\"" + s + "\"";
            case JsonArray(var arr) -> {
                var elements = arr.stream()
                    .map(JsonParser::toJsonString)
                    .collect(Collectors.joining(","));
                yield "[" + elements + "]";
            }
            case JsonObject(var props) -> {
                var entries = props.entrySet().stream()
                    .map(e -> "\"" + e.getKey() + "\":" + toJsonString(e.getValue()))
                    .collect(Collectors.joining(","));
                yield "{" + entries + "}";
            }
        };
    }
}

约束与限制

包内可见性规则

permits子句中列出的类必须在同一个模块中,或者与sealed类在同一个包内:

java 复制代码
// 同一个包内
package com.example.shapes;

public sealed class Shape permits Circle, Rectangle {
    // ...
}

// 必须在同一个包内
final class Circle extends Shape { }
final class Rectangle extends Shape { }

不能与final同时使用

sealed和final是互斥的修饰符:

java 复制代码
// ❌ 错误:sealed和final不能同时使用
public sealed final class MyClass { }

// ✅ 正确:sealed类可以有final子类
public sealed class MyClass permits SubClass { }
final class SubClass extends MyClass { }

与传统模式的对比

特性 传统继承 sealed类
子类控制 无限制 精确控制
模式匹配穷举 无法保证 编译器保证
API设计 容易暴露实现 明确公开契约
向后兼容性 难以限制 通过permits控制

最佳实践

在API设计中优先使用sealed

java 复制代码
// 定义公开的API
public sealed interface Result<T> 
    permits Success, Failure {
    
    T getValue();
}

record Success<T>(T data) implements Result<T> {
    @Override
    public T getValue() {
        return data;
    }
}

record Failure<T>(String errorMessage) implements Result<T> {
    @Override
    public T getValue() {
        throw new IllegalStateException("Cannot get value from failure");
    }
}

结合record使用

record与sealed的配合是天作之合:

java 复制代码
public sealed interface HttpRequest 
    permits GetRequest, PostRequest, PutRequest, DeleteRequest {
    
    String path();
    Map<String, String> headers();
}

record GetRequest(
    String path, 
    Map<String, String> headers
) implements HttpRequest {}

record PostRequest(
    String path,
    Map<String, String> headers,
    String body
) implements HttpRequest {}

record PutRequest(
    String path,
    Map<String, String> headers,
    String body
) implements HttpRequest {}

record DeleteRequest(
    String path,
    Map<String, String> headers
) implements HttpRequest {}

使用non-sealed的时机

只在确实需要让第三方扩展时才使用non-sealed

java 复制代码
// 框架提供的抽象类,允许用户扩展
public sealed class FrameworkPlugin 
    permits DatabasePlugin, CachePlugin, non-sealed UserPlugin {
    
    public abstract void initialize();
}

// 允许用户自定义扩展
public class CustomPlugin extends FrameworkPlugin {
    @Override
    public void initialize() {
        // 用户自定义逻辑
    }
}

sealed不是要取代传统的继承,而是在开放和封闭之间提供了更精细的控制粒度。在Java 17及以后的版本中,sealed类与record、模式匹配等特性共同构成了现代Java函数式编程的基础,让Java语言在保持面向对象特性的同时,向更安全、更简洁的方向持续演进。

相关推荐
weixin_449290012 小时前
Python vs Go:优缺点对比
网络·python·golang
Flittly2 小时前
【SpringAIAlibaba新手村系列】(8)持久化会话与 Redis 内存管理
java·人工智能·spring boot·spring·ai
祈澈菇凉2 小时前
Next.js + OpenAI API 跑通一个带流式输出的聊天机器人
开发语言·javascript·机器人
东离与糖宝2 小时前
Java 干掉 Python 垄断!LangChain4j + PgVector 本地知识库开发全流程
java·人工智能
lsx2024062 小时前
MySQL 删除数据表
开发语言
前端程序猿i2 小时前
纯JS 导出 Excel 工具
开发语言·javascript·excel
东离与糖宝2 小时前
OpenClaw 企业级实战:Java 微服务集成 AI 智能体,自动处理业务流
java·人工智能
沐知全栈开发2 小时前
XML Schema 复合类型 - 仅含元素
开发语言
weixin_408099672 小时前
跨境电商OCR:3秒识别多语言商品标签
开发语言·图像处理·人工智能·后端·ocr·api·文字识别ocr