Menu
CoddyTech

密封类(Java 17+)

本课属于 Coddy Java 学习旅程的“面向对象编程”单元。第 52 课,共 87 课。

在传统的 Java 中,创建类后,任何其他类都可以继承它(除非你将它标记为 final)。但如果你想允许继承,又不想让任何人都能继承,该怎么办?Java 17 引入了密封类,让你能够精确控制哪些类可以继承你的类。

密封类使用 sealed 和 permits 关键字显式声明允许的子类:

sealed abstract class Shape permits Circle, Rectangle, Triangle {
    abstract double area();
}

final class Circle extends Shape {
    double radius;
    double area() { return Math.PI * radius * radius; }
}

final class Rectangle extends Shape {
    double width, height;
    double area() { return width * height; }
}

final class Triangle extends Shape {
    double base, height;
    double area() { return 0.5 * base * height; }
}

只有 Circle、Rectangle 和 Triangle 可以继承 Shape。任何其他尝试继承它的类都会导致编译错误。

每个允许的子类都必须使用以下三种修饰符之一:final(不能继续扩展)、sealed(继续限制链)、non-sealed(允许不受限制地扩展):

sealed class Vehicle permits Car, Truck { }

final class Car extends Vehicle { }           // 不能被继承
non-sealed class Truck extends Vehicle { }    // 任何类都可以继承 Truck

密封类与 switch 表达式中的模式匹配结合使用时尤其强大,因为编译器知道所有可能的子类型,并能验证是否覆盖了所有情况。当你需要在某个领域中对一组固定类型进行建模时,密封类非常适合,例如支付方式、响应类型或几何形状。

challenge icon

挑战

简单

让我们构建一个通知系统,通过限制应用中可以存在的通知类型来演示密封类。你将创建一个类层次结构,其中只允许特定的通知类型,并且每种类型都以不同的方式处理发送。

你将把代码分布在四个文件中:

  • Notification.java:创建一个密封抽象类,作为所有通知的基类。它只能允许三个子类:EmailNotification、SMSNotification 和 PushNotification。你的密封类需要一个受保护的字段 message(String)、一个用于初始化该字段的构造函数、一个获取消息的方法,以及一个抽象方法 deliver(),该方法返回一个描述通知发送方式的 String。
  • EmailNotification.java:创建一个继承 Notification 的 final 类。EmailNotification 有一个额外的私有字段 recipient(String),用于存储电子邮件地址。它的构造函数应接收 message 和 recipient,并调用父类构造函数 super()。deliver() 方法应返回:Sending email to [recipient]: [message]
  • SMSNotification.java:创建一个继承 Notification 的 non-sealed 类。如果将来有需要,其他类就可以继承它。SMSNotification 有一个额外的私有字段 phoneNumber(String)。它的构造函数接收 message 和电话号码。deliver() 方法应返回:Sending SMS to [phoneNumber]: [message]
  • PushNotification.java:创建一个继承 Notification 的 final 类。PushNotification 有一个额外的私有字段 deviceId(String)。它的构造函数接收 message 和设备 ID。deliver() 方法应返回:Sending push to device [deviceId]: [message]
  • Main.java:把通知系统组合起来!你将收到四个输入:一条消息(String)、一个电子邮件地址(String)、一个电话号码(String)和一个设备 ID(String)。

    使用同一条消息和各自的联系信息,分别创建一种通知。将这三种通知都存储在类型为 Notification[] 的数组中,以演示密封类中的多态。然后遍历数组,打印对每个通知调用 deliver() 的结果。

你将按顺序收到四个输入:消息、电子邮件地址、电话号码和设备 ID。

注意密封类如何限制类层次结构:只有三个被允许的类可以继承 Notification。每个子类都必须声明为 final、sealed 或 non-sealed。这样你就能完全控制类型层次结构,同时仍然可以使用多态!

亲手试试

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // 读取 4 个输入
        String message = scanner.nextLine();
        String email = scanner.nextLine();
        String phoneNumber = scanner.nextLine();
        String deviceId = scanner.nextLine();
        
        // TODO:使用相同的 message,为每种通知类型各创建一个对象
        // 用 message 和 email 创建 EmailNotification
        // 用 message 和 phoneNumber 创建 SMSNotification
        // 用 message 和 deviceId 创建 PushNotification
        
        // TODO:将这 3 个通知存入 Notification[] 数组
        // 这演示了密封类的多态性
        
        // TODO:遍历数组,打印每个通知调用 deliver() 的结果
        // 针对每个通知
        
    }
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: Java 在线编译器