文章目录
Java 作为一种面向对象编程语言,提供了继承机制来实现代码复用和扩展。然而,无限制的继承可能导致代码库变得难以维护,甚至引入安全隐患。为了应对这一挑战,Java引入了密封类的概念。密封类允许开发者明确指定哪些类可以继承该类,从而创建一个封闭且可控的类层次结构。这不仅提高了代码的安全性和可维护性,还为模式匹配等高级特性铺平了道路。
语法说明
定义密封类
密封类是通过 sealed
关键字定义的,并且必须在 permits
子句中列出所有直接子类。这个列表是静态的,意味着一旦编译完成,不能添加新的子类到这个列表中。
public sealed class Shape permits Circle, Rectangle, Square {
// 类体
}
这段代码定义了一个名为 Shape
的密封类,它只允许 Circle
, Rectangle
, 和 Square
直接继承它。
定义子类
每个被允许的子类都必须明确声明它们是否可以被进一步继承:
-
最终类 (
final
)
最终类不能再被其他类继承,因此提供了一种绝对的安全性。public final class Circle extends Shape { private double radius; public Circle(double radius) { this.radius = radius; } public double getRadius() { return radius; } }
-
密封类 (
sealed
)
这里的Rectangle
类仍然是密封的,并且进一步限定了其直接子类。public sealed class Rectangle extends Shape permits RoundedRect { private double width; private double height; public Rectangle(double width, double height) { this.width = width; this.height = height; } public double getWidth() { return width; } public double getHeight() { return height; } }
-
非密封类 (
non-sealed
)
非密封类打破了密封性,意味着它可以被任何类继承。public non-sealed class Square extends Shape { private double side; public Square(double side) { this.side = side; } public double getSide() { return side; } }
使用场景探讨
密封类非常适合用来表示有限但封闭的类型层次结构,比如表达式树、状态机的状态、数据类型的建模等。例如,在一个图形编辑器中,形状的种类是有限的,我们可以使用密封类来确保只有特定类型的形状能够被创建。
此外,密封类也适用于API设计,防止外部代码随意扩展你的API,确保API的行为可控;或者用于框架开发,为框架开发者提供一种限制实现方式的方法,使得框架更加安全和易于维护。
实际应用示例
假设我们正在构建一个图形编辑器,其中形状是有限的,我们可以使用密封类来确保只有特定类型的形状能够被创建。同时,利用密封类与模式匹配结合的优势,简化形状描述逻辑,保证代码的安全性和完整性。
// 定义密封类 Shape
public sealed class Shape permits Circle, Rectangle, Square {}
// 定义最终类 Circle
public final class Circle extends Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
public double getRadius() {
return radius;
}
}
// 定义密封类 Rectangle
public sealed class Rectangle extends Shape permits RoundedRect {
private double width;
private double height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
public double getWidth() {
return width;
}
public double getHeight() {
return height;
}
}
// 定义非密封类 Square
public non-sealed class Square extends Shape {
private double side;
public Square(double side) {
this.side = side;
}
public double getSide() {
return side;
}
}
// 定义 RoundedRect 类
public final class RoundedRect extends Rectangle {
private double cornerRadius;
public RoundedRect(double width, double height, double cornerRadius) {
super(width, height);
this.cornerRadius = cornerRadius;
}
public double getCornerRadius() {
return cornerRadius;
}
}
// 使用模式匹配来描述形状
public String describeShape(Shape shape) {
return switch (shape) {
case Circle c -> "A circle with radius " + c.getRadius();
case Rectangle r -> "A rectangle with width " + r.getWidth() + " and height " + r.getHeight();
case Square s -> "A square with side " + s.getSide();
case RoundedRect rr -> "A rounded rectangle with width " + rr.getWidth() +
", height " + rr.getHeight() +
", and corner radius " + rr.getCornerRadius();
};
}
在这个例子中,我们展示了如何使用密封类来创建一个封闭的形状层次结构,并通过模式匹配简化对这些形状的处理逻辑。这种方式不仅提高了代码的可读性和安全性,还增强了代码的可维护性。
与其他语言特性的结合使用
密封类与模式匹配的结合是一个强大的组合,特别是从Java 17开始支持的模式匹配特性。通过密封类,编译器可以验证 switch
表达式是否覆盖了所有可能的情况,如果未覆盖,则会给出编译错误。这有助于防止运行时错误,并确保代码的健壮性。
此外,密封类还可以与访问修饰符(如 private
, protected
, public
)、泛型、注解等特性结合使用,以满足不同的设计需求。