Java 17 中的 sealed classes 与模式匹配如何简化多态实现?

在 Java 17 之前,使用传统的类层次结构来实现多态往往需要编写大量的 instanceof 检查或 switch 语句,并手工维护类之间的关系。自从 Java 15 引入了 sealed classes(封闭类)以及在 Java 17 强化了模式匹配(Pattern Matching),我们可以在保持代码安全、可读的同时,极大地简化多态实现。下面我们通过一个完整的示例来说明这一点。

1. 背景:传统多态实现的痛点

abstract class Shape { }
class Circle extends Shape { }
class Rectangle extends Shape { }

public double area(Shape s) {
    if (s instanceof Circle c) {
        return Math.PI * c.radius * c.radius;
    } else if (s instanceof Rectangle r) {
        return r.width * r.height;
    } else {
        throw new IllegalArgumentException("Unknown shape");
    }
}
  • 耦合度高:每新增一种形状,就需要在 area 方法中加入新的分支。
  • 可维护性差:业务逻辑与类型检查混在一起,阅读不够直观。
  • 安全性不足:如果忘记处理某个子类,编译器不会提醒。

2. 使用 sealed 简化类层次

sealed 声明告诉编译器哪些子类是合法的,保证所有合法子类都已被列举:

public sealed abstract class Shape permits Circle, Rectangle {
    // common properties or methods
}

public final class Circle extends Shape {
    final double radius;
    public Circle(double radius) { this.radius = radius; }
}

public final class Rectangle extends Shape {
    final double width, height;
    public Rectangle(double w, double h) { this.width = w; this.height = h; }
}
  • CircleRectangle 必须被声明为 final 或者再次 sealed(避免进一步派生)。
  • 编译器会强制检查 Shape 的所有合法子类,防止遗漏。

3. 结合模式匹配的 switch 语法

Java 17 对 switch 语法进行了增强,允许我们在 switch 中直接使用模式匹配:

public double area(Shape s) {
    return switch (s) {
        case Circle c -> Math.PI * c.radius * c.radius;
        case Rectangle r -> r.width * r.height;
    };
}
  • 简洁:不需要 instanceof 语句。
  • 安全:因为 Shapesealed,编译器会检查所有分支是否已覆盖所有合法子类。若未覆盖,编译会报错。
  • 可读:逻辑清晰,一眼就能看出每种形状的处理方式。

4. 进一步提升:使用 record 记录类

如果形状类仅仅是数据持有者,可以用 record 来简化定义:

public sealed record Shape permits Circle, Rectangle { }

public sealed record Circle(double radius) extends Shape { }
public sealed record Rectangle(double width, double height) extends Shape { }
  • record 自动生成构造器、equalshashCodetoString
  • 代码更短、更易维护。

5. 小结

  • sealed:让类层次更明确,避免隐藏的子类导致的不可预期行为。
  • 模式匹配:让 switch 语句更强大,直接在分支中解构对象。
  • record:提供更简洁的数据持有类,配合 sealed 可以快速搭建安全、易读的类型体系。

当你在项目中需要处理多态时,推荐先使用 sealed 来定义有限的子类集合,然后利用模式匹配的 switch 来实现业务逻辑。这样既能保持代码的安全性,也能显著提升可维护性和可读性。

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注