在 Java 17 中,密封类(Sealed Classes)正式成为语言规范的一部分,旨在帮助开发者更好地控制类的继承结构。本文将从概念讲起,逐步演示如何在实际项目中使用密封类,实现更安全、更易维护的代码。
一、密封类到底是什么?
密封类是对类继承层次进行限制的一种机制。与传统的 final 类不同,密封类允许你指定一组合法的子类或实现接口。被声明为密封类的类或接口只能被这些指定的子类继承或实现,其他任何类都无法扩展。
public sealed interface Shape permits Circle, Square, Rectangle {
double area();
}
上面代码中,Shape 只允许 Circle、Square、Rectangle 这三个类实现。任何尝试继承或实现 Shape 的其它类都会导致编译错误。
二、为什么要使用密封类?
- 控制继承结构
通过明确声明哪些类可以继承,减少潜在的误用或误解。 - 增强类型安全
编译器能够在编译期发现非法的继承关系,提升代码安全性。 - 改进代码可读性
代码阅读者一眼就能知道合法的子类,理解类层次结构更加清晰。 - 利于 pattern matching
结合 Java 20 的sealed class与record,可以更好地配合switch表达式的 pattern matching。
三、实际使用案例
下面以一个简单的支付系统为例,演示如何使用密封类来约束支付方式的实现。
1. 定义密封支付接口
public sealed interface PaymentMethod permits CreditCard, PayPal, ApplePay {
boolean process(double amount);
}
2. 实现具体支付方式
public final class CreditCard implements PaymentMethod {
private final String cardNumber;
private final String cardHolder;
public CreditCard(String cardNumber, String cardHolder) {
this.cardNumber = cardNumber;
this.cardHolder = cardHolder;
}
@Override
public boolean process(double amount) {
System.out.printf("Processing credit card payment: %.2f%n", amount);
// 简化演示,实际逻辑请实现安全加密与调用支付网关
return true;
}
}
public final class PayPal implements PaymentMethod {
private final String email;
public PayPal(String email) {
this.email = email;
}
@Override
public boolean process(double amount) {
System.out.printf("Processing PayPal payment: %.2f%n", amount);
return true;
}
}
public final class ApplePay implements PaymentMethod {
private final String deviceToken;
public ApplePay(String deviceToken) {
this.deviceToken = deviceToken;
}
@Override
public boolean process(double amount) {
System.out.printf("Processing Apple Pay payment: %.2f%n", amount);
return true;
}
}
注意:上述实现类使用
final修饰,进一步保证它们不能被继承。若你想允许这些类继续被子类化,只需把final去掉即可。
3. 业务层面使用
public class PaymentService {
public boolean execute(PaymentMethod method, double amount) {
return method.process(amount);
}
}
4. 测试
public class Demo {
public static void main(String[] args) {
PaymentService service = new PaymentService();
PaymentMethod cc = new CreditCard("4111111111111111", "Alice");
PaymentMethod pp = new PayPal("[email protected]");
PaymentMethod ap = new ApplePay("device-token-123");
service.execute(cc, 100.00);
service.execute(pp, 200.00);
service.execute(ap, 300.00);
}
}
运行结果:
Processing credit card payment: 100.00
Processing PayPal payment: 200.00
Processing Apple Pay payment: 300.00
四、密封类的高级使用技巧
1. 结合 record 进行值对象封装
public sealed record PaymentDetails(double amount, String currency) permits CreditCard, PayPal, ApplePay {}
2. 使用 switch 表达式与 pattern matching
double total = switch (method) {
case CreditCard cc -> cc.process(amount) ? amount : 0;
case PayPal pp -> pp.process(amount) ? amount : 0;
case ApplePay ap -> ap.process(amount) ? amount : 0;
};
3. 在测试中验证非法继承
// 下面这行代码会在编译期报错
class UnknownPayment implements PaymentMethod { /* ... */ }
这正是密封类的价值所在——在编译时就把非法的继承关系拒之门外。
五、总结
- 密封类 为 Java 提供了一种在编译期约束继承层次的机制。
- 通过
permits关键字可以精确控制哪些类可以继承或实现。 - 在实际项目中,密封类能帮助我们减少错误、提升代码安全性,并使层次结构更易理解。
- 与
record、switch等新特性配合使用,可进一步提升代码的表达力。
小提示:如果你在使用 IDE(如 IntelliJ IDEA、Eclipse)时发现
permits关键字不被识别,请确认 JDK 版本至少为 17,并在项目的编译设置中将源码级别设置为 17 或更高。
希望这篇文章能帮助你快速上手 Java 17 的密封类,为你的项目带来更好的可维护性与安全性。祝编码愉快!

发表回复