下文笔者讲述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 希望防止未经授权的扩展;
- 与模式匹配结合,实现逻辑的穷尽性处理;
- 替代枚举,处理更复杂的状态或行为场景。
通过合理使用密封类,
可以让代码的类型层次更加清晰、稳定,减少因不当继承导致的潜在问题,提升整体代码质量
版权声明
本文仅代表作者观点,不代表本站立场。
本文系作者授权发表,未经许可,不得转载。


