密封类(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 表达式中的模式匹配结合使用时尤其强大,因为编译器知道所有可能的子类型,并能验证是否覆盖了所有情况。当你需要在某个领域中对一组固定类型进行建模时,密封类非常适合,例如支付方式、响应类型或几何形状。
挑战
简单让我们构建一个通知系统,通过限制应用中可以存在的通知类型来演示密封类。你将创建一个类层次结构,其中只允许特定的通知类型,并且每种类型都以不同的方式处理发送。
你将把代码分布在四个文件中:
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() 的结果
// 针对每个通知
}
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: Java 在线编译器