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教程。