Java 密封类
上次修改于 2025 年 5 月 28 日
本文介绍了 Java 中的密封类,这是 Java 17 中引入的一项功能,旨在强制执行受控的继承层次结构。 密封类允许开发人员限制哪些子类可以扩展给定的类,从而确保更好的设计执行并防止意外的继承。
密封类 提供了对类扩展的显式控制,仅允许指定的子类型继承它们。 这种方法增强了领域建模,强制执行对类层次结构的约束。
密封类的主要特征
- 使用
sealed
修饰符声明,以指示受限的继承。 - 使用
permits
关键字指定允许的子类。 - 允许的子类型必须与密封类位于同一模块或包中。
- 子类型必须显式扩展密封类并声明它们的继承类型。
基本语法
密封类定义了一组固定的可以扩展它的子类,如下所示。 这种方法确保只有显式允许的类型才能参与继承层次结构,从而在代码库中提供更大的控制和可预测性。
public sealed class Vehicle permits Car, Truck { // class members }
允许的子类型必须指定以下继承类型之一
final
- 阻止任何进一步的子类化。sealed
- 允许子类化,但仅适用于指定的子类型。non-sealed
- 删除所有限制,允许不受限制的继承。
密封类提供了以下几个优点
- 封装业务逻辑 - 防止不必要的子类化。
- 增强安全性 - 通过限制子类修改来保护 API。
- 提高代码可维护性 - 通过定义固定的层次结构来降低复杂性。
- 更好的编译器检查 - 帮助及早发现意外的继承问题。
简单密封类示例
具有实现的简单密封类。 此示例演示如何声明密封类及其允许的子类,说明了在实践中强制执行受控继承的方式。
sealed abstract class Shape permits Circle, Rectangle { abstract double area(); } final class Circle extends Shape { private final double radius; Circle(double radius) { this.radius = radius; } @Override double area() { return Math.PI * radius * radius; } } final class Rectangle extends Shape { private final double width, height; Rectangle(double width, double height) { this.width = width; this.height = height; } @Override double area() { return width * height; } } void main() { Shape circle = new Circle(5.0); Shape rectangle = new Rectangle(4.0, 6.0); System.out.println("Circle area: " + circle.area()); System.out.println("Rectangle area: " + rectangle.area()); }
在此示例中,我们定义了一个密封的抽象类 Shape
,它只允许 Circle
和 Rectangle
作为其子类。 Shape
类有一个抽象方法 area
,它必须由其子类实现。 Circle
和 Rectangle
类被标记为 final
,这意味着它们不能进一步扩展。 这种结构确保 Shape
层次结构保持受控和可预测,只允许指定的子类参与继承链。
密封类层次结构
创建密封类的层次结构。 本节展示了如何跨多个级别扩展密封类,从而允许复杂但定义明确的继承结构。
sealed class Vehicle permits Car, Truck { protected String manufacturer; Vehicle(String manufacturer) { this.manufacturer = manufacturer; } } sealed class Car extends Vehicle permits ElectricCar { Car(String manufacturer) { super(manufacturer); } } final class ElectricCar extends Car { ElectricCar(String manufacturer) { super(manufacturer); } } final class Truck extends Vehicle { Truck(String manufacturer) { super(manufacturer); } } void main() { Vehicle tesla = new ElectricCar("Tesla"); Vehicle ford = new Truck("Ford"); System.out.println(tesla.manufacturer); System.out.println(ford.manufacturer); }
此示例演示了一个密封类 Vehicle
,它有两个允许的子类:Car
和 Truck
。 Car
类是密封的,只允许 ElectricCar
作为子类。 这种结构允许一个受控的层次结构,其中只有指定的子类可以扩展 Vehicle
类,从而确保继承保持可预测和可管理。
与密封类进行模式匹配
使用密封类进行模式匹配。 使用密封类进行模式匹配可以简洁而详尽地处理所有可能的子类型,使您的代码更安全且更易于维护。
sealed class Expr permits Constant, Add, Subtract { abstract int eval(); } final class Constant extends Expr { private final int value; Constant(int value) { this.value = value; } @Override int eval() { return value; } } final class Add extends Expr { private final Expr left, right; Add(Expr left, Expr right) { this.left = left; this.right = right; } @Override int eval() { return left.eval() + right.eval(); } } void main() { Expr expr = new Add(new Constant(5), new Constant(3)); System.out.println("Result: " + eval(expr)); } int eval(Expr e) { return switch (e) { case Constant c -> c.eval(); case Add a -> eval(a.left) + eval(a.right); case Subtract s -> eval(s.left) - eval(s.right); }; }
密封类支持穷尽的模式匹配,因为所有子类型都是已知的。 此功能允许编译器验证是否处理了所有情况,从而降低了由于未处理的子类而导致运行时错误的风险。
密封接口
Java 还允许密封接口,从而提供相同的控制来决定哪些类或接口可以实现或扩展它们。 密封接口对于建模需要限制类和接口扩展的层次结构很有用,从而确保一组固定的允许实现者。 这种机制有助于维护类型系统中的严格边界并支持健壮的领域建模。
sealed interface Shape permits Circle, Rectangle, Polygon { double area(); } final class Circle implements Shape { private final double radius; Circle(double radius) { this.radius = radius; } public double area() { return Math.PI * radius * radius; } } final class Rectangle implements Shape { private final double width, height; Rectangle(double width, double height) { this.width = width; this.height = height; } public double area() { return width * height; } } non-sealed class Polygon implements Shape { private final double area; Polygon(double area) { this.area = area; } public double area() { return area; } } void main() { Shape c = new Circle(3); Shape r = new Rectangle(4, 5); Shape p = new Polygon(12); System.out.println("Circle area: " + c.area()); System.out.println("Rectangle area: " + r.area()); System.out.println("Polygon area: " + p.area()); }
此示例演示了一个密封接口 Shape
,它有三个允许的实现者:一个 final 的 Circle
,一个 final 的 Rectangle
和一个 non-sealed 的 Polygon
,它可以进一步扩展。 密封接口提供与密封类相同的好处,包括穷尽的模式匹配和受控的扩展。 通过密封接口,您可以强制执行架构约束并提高代码的可靠性。
non-sealed 关键字
Java 中的 non-sealed
关键字允许密封类或接口的允许子类选择退出密封,使其可以进一步扩展。 这意味着,虽然父类限制了哪些类可以扩展它,但 non-sealed
子类可以被任何其他类自由扩展,从而取消了对其自身层次结构的限制。 当您希望允许类层次结构的某些分支在未来进行扩展,同时保持其他分支的严格控制时,这种灵活性非常有用。
sealed class Animal permits Dog, Cat, WildAnimal {} final class Dog extends Animal {} non-sealed class Cat extends Animal {} class PersianCat extends Cat {} class SiameseCat extends Cat {} non-sealed class WildAnimal extends Animal {} void main() { Cat genericCat = new Cat(); PersianCat persian = new PersianCat(); SiameseCat siamese = new SiameseCat(); System.out.println("Cat: " + genericCat.getClass().getSimpleName()); System.out.println("PersianCat: " + persian.getClass().getSimpleName()); System.out.println("SiameseCat: " + siamese.getClass().getSimpleName()); }
在此示例中,Animal
是一个密封类,它允许 Dog
、Cat
和 WildAnimal
作为子类。 Dog
是 final 的,不能进一步扩展。 Cat
被标记为 non-sealed
,因此它可以被任何类扩展,例如 PersianCat
和 SiameseCat
。 这演示了 non-sealed
关键字如何为特定的子类重新打开继承层次结构,从而在需要时提供更大的灵活性。
WildAnimal
类也被标记为 non-sealed
,允许它自由扩展。 这在层次结构中提供了灵活性,同时仍然保持对基类 Animal
的控制。 通过有选择地使用 non-sealed
,您可以在类设计中平衡严格性和可扩展性。
何时使用密封类
适当的用例包括需要对一组固定的实现进行建模、定义具有已知变体的域模型、限制 API 实现或利用模式匹配来穷尽处理所有可能的子类型的情况。 密封类在这些上下文中特别有用,因为它们提供了关于类型层次结构结构的编译时保证。
使用密封类的好处包括更好的领域建模、改进的 API 安全性、对穷尽模式匹配的支持以及更易于维护的代码。 通过限制继承,您可以防止意外的扩展,并使您的代码库更易于理解和维护。
来源
在本文中,我们探讨了用于受控继承的 Java 密封类。 它们通过受限的层次结构使代码更安全和更易于维护。
作者
列出所有Java教程。