Java 17 密封类(Sealed Classes)用法与场景

林欢喜 Java教程 发布时间:2025-11-21 10:16:47 阅读数:10756 1 密封类
下文笔者讲述java17中密封类的简介说明,如下所示

java17之密封类简介

 
密封类是Java 17正式引入一项重要特性
    它主要用于增强 Java 类型系统的表达力和安全性,
    通过明确指定哪些类可以继承或实现自己,
  从而在编译时就严格控制了类的继承层次结构,避免了不必要的扩展,
   使代码更加健壮、可维护

密封类定义

密封类使用 `sealed` 关键字修饰
   并且必须通过 `permits` 关键字明确列出所有允许继承它的子类(称为“许可子类)

密封类语法

 
public sealed class 密封类名 permits 许可子类1, 许可子类2, ... {
    // 类的成员定义
}

许可子类限制说明

许可子类必须满足以下条件:
- 必须与密封类在同一个包中(如果密封类是顶层类);
      如果密封类是嵌套类,则许可子类可以是同一外部类的其他嵌套类。
- 必须明确指定继承方式:
     要么是 `final`(不可再继承)
     要么是 `sealed`(继续限制子类)
     要么是 `non-sealed`(取消限制,允许任意继承)。

示例分享

// 密封类 Shape,仅允许 Circle、Square、Triangle 继承
public sealed class Shape permits Circle, Square, Triangle {
    public abstract double area();
}

// 许可子类 Circle,声明为 final(不可再继承)
public final class Circle extends Shape {
    private final double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

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

// 许可子类 Square,声明为 sealed(继续限制子类)
public sealed class Square extends Shape permits RedSquare, BlueSquare {
    protected final double side;

    public Square(double side) {
        this.side = side;
    }

    @Override
    public double area() {
        return side * side;
    }
}

// Square 的许可子类 RedSquare,声明为 final
public final class RedSquare extends Square {
    public RedSquare(double side) {
        super(side);
    }
}

// 许可子类 Triangle,声明为 non-sealed(允许任意继承)
public non-sealed class Triangle extends Shape {
    private final double base;
    private final double height;

    public Triangle(double base, double height) {
        this.base = base;
        this.height = height;
    }

    @Override
    public double area() {
        return 0.5 * base * height;
    }
}

// 可以自由继承 non-sealed 的 Triangle
public class EquilateralTriangle extends Triangle {
    public EquilateralTriangle(double side) {
        super(side, (Math.sqrt(3) / 2) * side);
    }
}

密封类核心特性

1. 严格控制继承:
     密封类明确指定允许的子类
    杜绝了未经授权的扩展,
   避免了继承层次的混乱。
2. 编译时安全:
    如果尝试创建一个未被 `permits` 列出的子类,
     编译器会直接报错。
3. 模式匹配友好:
    密封类与 Java 16 引入的模式匹配(`instanceof`)结合使用时,
     可以实现“穷尽性检查”,确保所有可能的子类都被处理,避免遗漏。

密封类适用场景

1. 定义固定类型层次结构

当业务场景中某个类的子类是明确且固定的,
   不希望被外部随意扩展时,密封类是最佳选择。

例:
-业务状态类: 如
     订单状态(`OrderStatus`),仅允许 `Pending`、`Paid`、`Shipped`、`Delivered` 这几种固定状态,使用密封类可以避免出现无效的状态子类。
-数据类型类: 如
    几何图形(`Shape`),仅支持圆形、正方形、三角形等已知类型,后续不会随意添加新图形,用密封类可以严格控制类型扩展。

例(订单状态)
public sealed class OrderStatus permits Pending, Paid, Shipped, Delivered {
    // 状态描述
    public abstract String getDescription();
}

public final class Pending extends OrderStatus {
    @Override
    public String getDescription() {
        return "订单待支付";
    }
}

public final class Paid extends OrderStatus {
    @Override
    public String getDescription() {
        return "订单已支付";
    }
}

public final class Shipped extends OrderStatus {
    @Override
    public String getDescription() {
        return "订单已发货";
    }
}

public final class Delivered extends OrderStatus {
    @Override
    public String getDescription() {
        return "订单已送达";
    }
}

2.增强 API稳定性和安全性

如果开发是一个公共 API
    不希望用户通过继承来修改或扩展 API 的核心类
     密封类可以防止API被滥用 
  Java 标准库中的 `java.lang.Throwable` 类(异常的根类)
     如果用密封类重新设计,可以明确指定哪些类可以作为异常子类,避免用户创建不符合规范的异常类型。

3.与模式匹配结合实现穷尽性处理

密封类与模式匹配(`instanceof`)结合时
   编译器会检查 `switch` 表达式是否覆盖了所有许可子类
    如果遗漏会编译报错,确保逻辑的完整性。
例:
public class ShapeProcessor {
    public static String getShapeInfo(Shape shape) {
        return switch (shape) {
            case Circle c -> String.format("圆形:半径=%.2f,面积=%.2f", c.radius(), c.area());
            case Square s -> String.format("正方形:边长=%.2f,面积=%.2f", s.side(), s.area());
            case Triangle t -> String.format("三角形:底=%.2f,高=%.2f,面积=%.2f", t.base(), t.height(), t.area());
            // 无需 default 分支,编译器会检查是否覆盖所有许可子类
        };
    }

    public static void main(String[] args) {
        Shape circle = new Circle(5);
        Shape square = new RedSquare(4);
        Shape triangle = new EquilateralTriangle(3);

        System.out.println(getShapeInfo(circle));    // 圆形:半径=5.00,面积=78.54
        System.out.println(getShapeInfo(square));    // 正方形:边长=4.00,面积=16.00
        System.out.println(getShapeInfo(triangle));  // 三角形:底=3.00,高=2.60,面积=3.90
    }
}

如果后续给 `Shape` 增加一个新的许可子类 `Rectangle`
    但忘记在 `switch` 表达式中处理,
    编译器会直接报错,提醒开发者补充逻辑,避免遗漏。

4.替代枚举的复杂场景

枚举适用于“常量集合",
   但如果每个常量需要携带不同的状态或行为(且可能需要继承扩展)
   密封类比枚举更灵活
  枚举 `Operation` 只能定义固定的常量和统一的方法
     而密封类 `Expression` 可以定义不同的表达式类型
      (如常量表达式、加法表达式、乘法表达式)
      每个类型有自己的属性和计算逻辑,
      且支持扩展(如果声明为 `non-sealed`)
// 密封类 Expression,允许 Constant、Addition、Multiplication 继承
public sealed class Expression permits Constant, Addition, Multiplication {
    public abstract int evaluate();
}

// 常量表达式
public final class Constant extends Expression {
    private final int value;

    public Constant(int value) {
        this.value = value;
    }

    @Override
    public int evaluate() {
        return value;
    }
}

// 加法表达式
public final class Addition extends Expression {
    private final Expression left;
    private final Expression right;

    public Addition(Expression left, Expression right) {
        this.left = left;
        this.right = right;
    }

    @Override
    public int evaluate() {
        return left.evaluate() + right.evaluate();
    }
}

// 乘法表达式
public final class Multiplication extends Expression {
    private final Expression left;
    private final Expression right;

    public Multiplication(Expression left, Expression right) {
        this.left = left;
        this.right = right;
    }

    @Override
    public int evaluate() {
        return left.evaluate() * right.evaluate();
    }
}

// 使用示例
public class ExpressionCalculator {
    public static void main(String[] args) {
        Expression expr = new Addition(
            new Constant(3),
            new Multiplication(new Constant(4), new Constant(5))
        );
        System.out.println(expr.evaluate()); // 3 + (4*5) = 23
    }
}

密封类注意事项

1. 许可子类的位置限制:
      顶层密封类的许可子类必须在同一个包中;
      嵌套密封类的许可子类可以是同一外部类的其他嵌套类。
2. 继承方式的明确性:
      许可子类必须声明为 `final`、`sealed` 或 `non-sealed`,
      不能省略,否则编译器报错。
3. 与抽象类的区别:
      密封类可以是抽象类(如示例中的 `Shape`),
      也可以是具体类;
      而抽象类不限制子类的数量和范围。
4. 兼容性:
      密封类是 Java 17 的特性,
      如果项目需要兼容低版本 Java(如 Java 8、11),则无法使用。

密封类大白话总结

密封类是Java 17 中一项强大的特性
     核心价值在于
     严格控制类的继承层次,增强代码的安全性和可维护性
它适用于以下场景:
- 类的子类是固定且明确的(如业务状态、固定类型集合);
- 公共 API 希望防止未经授权的扩展;
- 与模式匹配结合,实现逻辑的穷尽性处理;
- 替代枚举,处理更复杂的状态或行为场景。

通过合理使用密封类,
   可以让代码的类型层次更加清晰、稳定,减少因不当继承导致的潜在问题,提升整体代码质量
版权声明

本文仅代表作者观点,不代表本站立场。
本文系作者授权发表,未经许可,不得转载。

本文链接: https://www.Java265.com/JavaCourse/202511/8513.html

最近发表

热门文章

好文推荐

Java265.com

https://www.java265.com

站长统计|粤ICP备14097017号-3

Powered By Java265.com信息维护小组

使用手机扫描二维码

关注我们看更多资讯

java爱好者