Sealed class!谈谈JDK17的新特性——密封类

171 阅读3分钟

JDK 17 引入了密封类(Sealed Classes)的特性,它是 Java 15 中引入的密封类型(Sealed Types)的一部分,并在 JDK 17 中得到进一步的完善和改进。密封类和密封接口(Sealed Interfaces)提供了一种方式,用于限制哪些类或接口可以继承或实现它们。

密封类的概念

密封类是指一种限制其子类继承的类。通过声明一个类为密封类,开发者可以显式控制哪些类是该类的合法子类,而不允许其他任何类来继承它。这个特性为 Java 提供了更强的类型安全和可维护性。

如何声明密封类

密封类使用 sealed 关键字进行声明,并通过 permits 关键字列出允许继承它的子类。示例:

public sealed class Shape permits Circle, Rectangle { 
    // Shape 类是密封类,只允许 Circle 和 Rectangle 类继承它
}
​
public final class Circle extends Shape { 
    // Circle 是 Shape 类的一个合法子类
}
​
public final class Rectangle extends Shape {
    // Rectangle 是 Shape 类的另一个合法子类
}
​
public class Triangle extends Shape { 
    // 编译错误:Triangle 无法继承 Shape,因为它不是被允许的子类
}

密封类的好处

  1. 类型安全: 通过限制继承,密封类提供了更加严格的类型控制,避免了外部代码(或库)对类的扩展。这意味着开发者可以准确地知道所有的子类,并进行精确的类型检查,从而减少了潜在的错误。
  2. 更好的模式匹配: 密封类与 Java 17 引入的模式匹配(Pattern Matching)相结合时,能够使模式匹配更高效。例如,在 switch 语句中使用密封类时,编译器可以确保覆盖所有合法子类,从而提高代码的可靠性和清晰性。
  3. 代码可维护性: 密封类帮助开发者控制继承结构,使得继承关系更具可预测性和可控性。在大型系统中,随着继承体系的扩展,控制哪些类可以继承某个类能有效减少意外继承带来的问题。
  4. 性能优化: 密封类在编译时就能确定其继承层级,从而可以为 JVM 进行优化。这有助于提升性能,尤其是在涉及模式匹配和虚拟方法调用的场景中。

密封类的限制

  1. 子类数量有限: 密封类只能允许指定的一组类继承,因此比普通类有更多的限制。这使得它不适合所有的继承场景。
  2. 不能无限制地继承: 密封类不允许其他类无条件地继承或实现,因此其继承树是有限的,这与普通类完全开放继承的方式不同。

例子:用密封类实现一个“形状”层次结构

假设我们需要设计一个形状系统,只有圆形、矩形和三角形是合法的形状。可以通过密封类来实现:

public sealed class Shape permits Circle, Rectangle, Triangle {
    public abstract double area();
}
​
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;
    }
}
​
public final class Rectangle extends Shape {
    private final double length;
    private final double width;
    
    public Rectangle(double length, double width) {
        this.length = length;
        this.width = width;
    }
​
    @Override
    public double area() {
        return length * width;
    }
}
​
public final 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;
    }
}

使用示例:

public class ShapeTest {
    public static void main(String[] args) {
        Shape circle = new Circle(5.0);
        Shape rectangle = new Rectangle(4.0, 6.0);
        Shape triangle = new Triangle(3.0, 7.0);
​
        System.out.println("Circle area: " + circle.area());
        System.out.println("Rectangle area: " + rectangle.area());
        System.out.println("Triangle area: " + triangle.area());
    }
}

在这个例子中,Shape 是一个密封类,它只允许 CircleRectangleTriangle 作为其子类。每个子类都重写了 area() 方法,计算各自形状的面积。

总结

密封类是 Java 17 引入的重要特性之一,它为开发者提供了更加严格的类型控制、增强了代码的可维护性和类型安全性,并且可以与模式匹配等其他新特性一起使用,提升了编程的效率和可靠性。