当前位置: 首页 > news >正文

Java 17 密封类(Sealed Classes)实战:从类型安全到架构解耦的范式升级

在 Java 17 之前,面向对象编程中的 “类继承” 一直存在一个痛点:一个公开类(public class)可以被任何其他类无限制继承,导致类型体系失控、代码维护成本升高。例如,定义一个 “支付方式” 基类后,开发者可能会随意继承出不符合业务逻辑的子类(如 “现金支付” 继承 “电子支付”),破坏类型体系的完整性。为解决这一问题,Java 17 正式将密封类(Sealed Classes) 纳入标准特性,通过 “显式指定子类范围”“限制继承权限”“强制类型覆盖”,从语言层面实现了类型体系的可控性,为枚举、接口等特性提供了更灵活的补充。本文将从传统类继承的痛点出发,详解密封类的核心概念、语法规则、实战场景及与其他 Java 特性的结合,帮你掌握这一革新性语言特性的最佳实践。

一、为什么需要密封类?—— 传统类继承的 4 大痛点

在理解密封类之前,我们首先要明确:传统 Java 的类继承机制虽然灵活,但在复杂业务场景下,会暴露诸多问题,尤其是在需要严格控制类型体系的场景中。

1.1 痛点 1:类型体系失控(无限制继承)

传统公开类(public class)的继承权限完全开放,任何模块、任何类都可以继承它并扩展功能,导致类型体系混乱。例如,定义一个 “订单状态” 基类:

 

// 传统公开基类:订单状态

public class OrderStatus {

private String statusCode;

private String statusDesc;

// 构造器、getter、setter

public OrderStatus(String statusCode, String statusDesc) {

this.statusCode = statusCode;

this.statusDesc = statusDesc;

}

// 抽象方法:处理订单状态变更

public abstract void handleStatusChange(Order order);

}

理论上,订单状态应仅包含 “待支付”“已支付”“已取消”“已完成” 4 种,但由于OrderStatus是公开类,其他开发者可能会随意继承出不符合业务逻辑的子类:

 

// 错误:随意继承OrderStatus,新增“退款中”状态(实际应属于另一类型体系)

public class RefundingStatus extends OrderStatus {

public RefundingStatus() {

super("REFUNDING", "退款中");

}

@Override

public void handleStatusChange(Order order) {

// 处理退款逻辑(与订单状态逻辑冲突)

}

}

这种无限制继承会导致订单状态体系失控,后续在处理订单状态时(如switch判断、状态流转校验),需要额外处理非预期的子类,增加代码复杂度和 bug 风险。

1.2 痛点 2:类型判断不安全(instanceof 滥用)

由于传统类继承无法限制子类范围,在处理类型判断时,往往需要使用instanceof进行多次判断,且无法保证覆盖所有子类,导致类型判断不安全。例如,处理订单状态变更:

 

// 传统方式:使用instanceof判断订单状态,无法保证覆盖所有子类

public void processOrderStatus(OrderStatus status, Order order) {

if (status instanceof PendingPaymentStatus) {

// 处理待支付状态

((PendingPaymentStatus) status).handleStatusChange(order);

} else if (status instanceof PaidStatus) {

// 处理已支付状态

((PaidStatus) status).handleStatusChange(order);

} else if (status instanceof CancelledStatus) {

// 处理已取消状态

((CancelledStatus) status).handleStatusChange(order);

} else if (status instanceof CompletedStatus) {

// 处理已完成状态

((CompletedStatus) status).handleStatusChange(order);

} else {

// 处理非预期状态(新增子类后易遗漏)

throw new IllegalArgumentException("不支持的订单状态:" + status.getStatusCode());

}

}

这种方式存在两个问题:

  1. 遗漏风险:新增OrderStatus子类后,若未同步更新processOrderStatus方法,会抛出异常;
  1. 类型转换冗余:每次instanceof判断后,都需要强制类型转换,代码冗余。

1.3 痛点 3:枚举的灵活性不足

枚举(enum)是传统 Java 中控制类型范围的常用方式,但其灵活性不足 —— 枚举常量无法继承其他类,也无法实现复杂的状态逻辑。例如,用枚举定义订单状态:

 

// 枚举定义订单状态:无法实现复杂的状态处理逻辑

public enum OrderStatusEnum {

PENDING_PAYMENT("PENDING", "待支付"),

PAID("PAID", "已支付"),

CANCELLED("CANCELLED", "已取消"),

COMPLETED("COMPLETED", "已完成");

private String statusCode;

private String statusDesc;

// 构造器、getter

OrderStatusEnum(String statusCode, String statusDesc) {

this.statusCode = statusCode;

this.statusDesc = statusDesc;

}

// 枚举方法:处理状态变更(逻辑简单,无法扩展复杂逻辑)

public void handleStatusChange(Order order) {

switch (this) {

case PENDING_PAYMENT:

// 待支付逻辑(简单)

order.setPaymentDeadline(LocalDateTime.now().plusHours(2));

break;

case PAID:

// 已支付逻辑(简单)

order.setPaidTime(LocalDateTime.now());

break;

// 其他状态逻辑...

default:

throw new IllegalArgumentException("不支持的状态");

}

}

}

若 “待支付” 状态需要处理复杂逻辑(如发送支付提醒、校验库存),枚举的handleStatusChange方法会变得臃肿,且无法通过继承扩展,只能修改枚举类本身,违反 “开闭原则”。

1.4 痛点 4:接口的实现不可控

接口(interface)虽然可以定义方法契约,但无法限制实现类的范围 —— 任何类都可以实现接口,导致接口的实现类体系失控。例如,定义一个 “支付方式” 接口:

 

// 支付方式接口

public interface PaymentMethod {

BigDecimal calculateFee(BigDecimal amount); // 计算支付手续费

boolean processPayment(Order order); // 处理支付

}

理论上,支付方式应仅包含 “支付宝”“微信支付”“银联支付” 3 种,但其他开发者可能会实现不符合业务逻辑的类:

 

// 错误:实现PaymentMethod接口,新增“现金支付”(实际无需计算手续费)

public class CashPayment implements PaymentMethod {

@Override

public BigDecimal calculateFee(BigDecimal amount) {

return BigDecimal.ZERO; // 现金支付无手续费(与接口设计初衷冲突)

}

@Override

public boolean processPayment(Order order) {

// 处理现金支付(无需调用第三方接口,与其他支付方式逻辑差异大)

order.setPaymentMethod("CASH");

return true;

}

}

这种不可控的实现会导致在处理支付方式时(如统一计算手续费、统一异常处理),需要额外适配非预期的实现类,增加代码复杂度。

1.2 密封类的核心价值

Java 17 引入的密封类,通过以下 4 点设计,从根本上解决了传统类继承的痛点:

  1. 显式子类范围:密封类通过permits关键字显式指定允许继承的子类,禁止其他类继承;
  1. 类型安全判断:结合 Java 14 引入的 “模式匹配switch”,密封类支持编译期校验类型覆盖,避免instanceof滥用;
  1. 灵活的状态逻辑:密封类的子类可以继承其他类、实现复杂逻辑,弥补枚举的灵活性不足;
  1. 可控的实现体系:密封接口(Sealed Interfaces)可以限制实现类范围,解决接口实现不可控的问题。

二、密封类的核心概念与语法规则

密封类的使用围绕 “基类密封”“子类授权”“类型覆盖” 3 个核心概念展开,其语法规则简洁但严谨,掌握这些规则是使用密封类的基础。

2.1 1. 密封类的定义:sealed与permits

密封类通过sealed关键字修饰基类,并通过permits关键字显式指定允许继承的子类,语法格式如下:

 

// 密封类定义格式

sealed class 基类名 permits 子类1, 子类2, 子类3 {

// 类成员(字段、方法、构造器)

}

关键规则:
  1. sealed修饰符:基类必须用sealed修饰,表示该类是密封的,仅允许permits指定的子类继承;
  1. permits子句:permits后必须列出所有允许继承的子类,子类必须与基类在同一模块(Module)中;若基类与子类在同一包中,且子类是顶级类(Top-Level Class),permits子句可省略(编译器会自动推断);
  1. 子类的修饰符限制:密封类的子类必须使用以下 3 种修饰符之一,明确其继承权限:
    • final:子类不可再继承(最终类);
    • sealed:子类本身也是密封类,允许其指定的子类继承;
    • non-sealed:子类是开放的,允许任何类继承(打破密封)。
示例:定义密封类 “订单状态”
 

// 密封基类:OrderStatus,允许4个子类继承

sealed class OrderStatus permits PendingPaymentStatus, PaidStatus, CancelledStatus, CompletedStatus {

protected String statusCode;

protected String statusDesc;

// 构造器(子类必须调用)

public OrderStatus(String statusCode, String statusDesc) {

this.statusCode = statusCode;

this.statusDesc = statusDesc;

}

// 抽象方法:处理状态变更(子类必须实现)

public abstract void handleStatusChange(Order order);

// getter

public String getStatusCode() { return statusCode; }

public String getStatusDesc() { return statusDesc; }

}

// 子类1:待支付状态(final,不可再继承)

final class PendingPaymentStatus extends OrderStatus {

public PendingPaymentStatus() {

super("PENDING", "待支付");

}

@Override

public void handleStatusChange(Order order) {

// 复杂逻辑:设置支付截止时间、发送支付提醒

order.setPaymentDeadline(LocalDateTime.now().plusHours(2));

notificationService.sendPaymentReminder(order.getUserId(), order.getOrderId());

}

}

// 子类2:已支付状态(final)

final class PaidStatus extends OrderStatus {

public PaidStatus() {

super("PAID", "已支付");

}

@Override

public void handleStatusChange(Order order) {

// 复杂逻辑:记录支付时间、扣减库存

order.setPaidTime(LocalDateTime.now());

inventoryService.deductStock(order.getOrderItems());

}

}

// 子类3:已取消状态(final)

final class CancelledStatus extends OrderStatus {

public CancelledStatus() {

super("CANCELLED", "已取消");

}

@Override

public void handleStatusChange(Order order) {

// 复杂逻辑:记录取消原因、恢复库存

order.setCancelledTime(LocalDateTime.now());

inventoryService.restoreStock(order.getOrderItems());

}

}

// 子类4:已完成状态(final)

final class CompletedStatus extends OrderStatus {

public CompletedStatus() {

super("COMPLETED", "已完成");

}

@Override

public void handleStatusChange(Order order) {

// 复杂逻辑:记录完成时间、发送完成通知

order.setCompletedTime(LocalDateTime.now());

notificationService.sendCompletionNotice(order.getUserId(), order.getOrderId());

}

}

2.2 2. 密封接口(Sealed Interfaces)

除了密封类,Java 17 还支持密封接口 —— 通过sealed修饰接口,permits指定允许实现的类或子接口,解决传统接口实现不可控的问题。

语法格式:
 

// 密封接口定义格式

sealed interface 接口名 permits 实现类1, 实现类2, 子接口1 {

// 接口方法

}

关键规则:
  1. 实现类的修饰符限制:密封接口的实现类必须使用final、sealed或non-sealed修饰;
  1. 子接口的修饰符限制:密封接口的子接口必须使用sealed或non-sealed修饰(不可用final,因接口不可实例化)。
示例:定义密封接口 “支付方式”
 

// 密封接口:PaymentMethod,允许3个实现类

sealed interface PaymentMethod permits AlipayPayment, WechatPayment, UnionPayPayment {

// 计算支付手续费(抽象方法)

BigDecimal calculateFee(BigDecimal amount);

// 处理支付(默认方法,可提供通用逻辑)

default boolean processPayment(Order order) {

// 通用逻辑:记录支付日志

paymentLogService.recordLog(order.getOrderId(), this.getClass().getSimpleName());

// 具体逻辑由实现类实现

return doProcessPayment(order);

}

// 抽象方法:具体支付处理

boolean doProcessPayment(Order order);

}

// 实现类1:支付宝支付(final)

final class AlipayPayment implements PaymentMethod {

private static final BigDecimal FEE_RATE = new BigDecimal("0.006"); // 手续费率0.6%

@Override

public BigDecimal calculateFee(BigDecimal amount) {

// 支付宝手续费:金额×0.6%,最低1元

BigDecimal fee = amount.multiply(FEE_RATE);

return fee.compareTo(BigDecimal.ONE) < 0 ? BigDecimal.ONE : fee;

}

@Override

public boolean doProcessPayment(Order order) {

// 调用支付宝API处理支付

AlipayResponse response = alipayClient.execute(new AlipayTradePayRequest(order.getOrderId(), order.getAmount()));

return response.isSuccess();

}

}

// 实现类2:微信支付(final)

final class WechatPayment implements PaymentMethod {

private static final BigDecimal FEE_RATE = new BigDecimal("0.006"); // 手续费率0.6%

@Override

public BigDecimal calculateFee(BigDecimal amount) {

// 微信支付手续费:金额×0.6%,无最低 fee

return amount.multiply(FEE_RATE);

}

@Override

public boolean doProcessPayment(Order order) {

// 调用微信支付API处理支付

WxPayResponse response = wxPayClient.pay(order.getOrderId(), order.getAmount());

return response.getReturnCode().equals("SUCCESS");

}

}

// 实现类3:银联支付(final)

final class UnionPayPayment implements PaymentMethod {

private static final BigDecimal FEE_RATE = new BigDecimal("0.005"); // 手续费率0.5%

@Override

public BigDecimal calculateFee(BigDecimal amount) {

// 银联支付手续费:金额×0.5%,最低0.5元

BigDecimal fee = amount.multiply(FEE_RATE);

return fee.compareTo(new BigDecimal("0.5")) < 0 ? new BigDecimal("0.5") : fee;

}

@Override

public boolean doProcessPayment(Order order) {

// 调用银联支付API处理支付

UnionPayResponse response = unionPayClient.processPayment(order.getOrderId(), order.getAmount());

return response.isSuccess();

}

}

2.3 3. 密封类与模式匹配switch(Java 14+)

密封类的核心优势之一是与 “模式匹配switch”(Java 14 引入,Java 17 正式标准化)结合,实现编译期类型覆盖校验,避免instanceof滥用。

传统instanceof方式 vs 模式匹配switch方式

传统方式(存在遗漏风险):

 

// 传统方式:使用instanceof判断订单状态,无法保证覆盖所有子类

public void processOrderStatus(OrderStatus status, Order order) {

if (status instanceof PendingPaymentStatus) {

((PendingPaymentStatus) status).handleStatusChange(order);

} else if (status instanceof PaidStatus) {

((PaidStatus) status).handleStatusChange(order);

} else if (status instanceof CancelledStatus) {

((CancelledStatus) status).handleStatusChange(order);

} else if (status instanceof CompletedStatus) {

((CompletedStatus) status).handleStatusChange(order);

} else {

throw new IllegalArgumentException("不支持的订单状态:" + status.getStatusCode());

}

}

模式匹配switch方式(编译期校验覆盖):

 

// 模式匹配switch:编译期校验是否覆盖所有OrderStatus子类,无遗漏风险

public void processOrderStatus(OrderStatus status, Order order) {

switch (status) {

case PendingPaymentStatus pendingStatus -> pendingStatus.handleStatusChange(order);

case PaidStatus paidStatus -> paidStatus.handleStatusChange(order);

case CancelledStatus cancelledStatus -> cancelledStatus.handleStatusChange(order);

case CompletedStatus completedStatus -> completedStatus.handleStatusChange(order);

// 无需default:编译器会校验是否覆盖所有子类,若新增子类未处理,编译报错

}

}

关键优势:
  1. 编译期校验:若OrderStatus新增子类(如RefundingStatus),且未在switch中处理,编译器会直接报错,避免运行时异常;
  1. 无需类型转换:模式匹配switch会自动将status转换为对应子类类型(如PendingPaymentStatus pendingStatus),无需手动强制转换;
  1. 代码简洁:用->替代case和break,减少模板代码,可读性更高。

三、密封类的实战场景:从业务需求到代码落地

密封类在实际开发中应用广泛,尤其适合 “严格控制类型体系” 的场景。本节将结合 4 个典型业务场景(状态机设计、支付方式管理、数据类型校验、接口实现管控),展示从需求分析到密封类代码实现的完整过程。

3.1 场景 1:订单状态机设计(密封类核心场景)

需求:设计订单状态机,仅允许 “待支付→已支付→已完成”“待支付→已取消” 两种流转路径,禁止其他非法流转(如 “已完成→已取消”),且每个状态需处理复杂的业务逻辑(如支付提醒、库存扣减)。

传统方案(枚举 + 大量if判断)
 

// 传统方案:枚举定义状态,if判断流转合法性

public enum OrderStatusEnum {

PENDING_PAYMENT("PENDING", "待支付"),

PAID("PAID", "已支付"),

CANCELLED("CANCELLED", "已取消"),

COMPLETED("COMPLETED", "已完成");

// 省略字段、构造器、getter

// 判断状态流转是否合法

public boolean isTransitionAllowed(OrderStatusEnum targetStatus) {

return switch (this) {

case PENDING_PAYMENT -> targetStatus == PAID || targetStatus == CANCELLED;

case PAID -> targetStatus == COMPLETED;

case CANCELLED, COMPLETED -> false; // 已取消/已完成不可流转

};

}

// 处理状态变更(逻辑臃肿,无法扩展)

public void handleStatusChange(Order order) {

switch (this) {

case PENDING_PAYMENT:

order.setPaymentDeadline(LocalDateTime.now().plusHours(2));

break;

case PAID:

order.setPaidTime(LocalDateTime.now());

inventoryService.deductStock(order.getOrderItems());

break;

case CANCELLED:

order.setCancelledTime(LocalDateTime.now());

inventoryService.restoreStock(order.getOrderItems());

break;

case COMPLETED:

order.setCompletedTime(LocalDateTime.now());

break;

}

}

}

// 使用枚举处理订单状态

public void changeOrderStatus(Order order, OrderStatusEnum targetStatus) {

OrderStatusEnum currentStatus = order.getStatus();

// 判断流转是否合法

if (!currentStatus.isTransitionAllowed(targetStatus)) {

throw new IllegalArgumentException("非法状态流转:" + currentStatus + "→" + targetStatus);

}

// 处理状态变更(逻辑在枚举中,无法扩展)

targetStatus.handleStatusChange(order);

// 更新订单状态

order.setStatus(targetStatus);

}

密封类方案(密封类 + 模式匹配switch)
 

// 1. 定义密封类OrderStatus,指定4个子类

sealed class OrderStatus permits PendingPaymentStatus, PaidStatus, CancelledStatus, CompletedStatus {

// 省略字段、构造器、getter

// 抽象方法:判断状态流转是否合法

public abstract boolean isTransitionAllowed(OrderStatus targetStatus);

// 抽象方法:处理状态变更

public abstract void handleStatusChange(Order order);

}

// 2. 实现各状态子类(每个子类封装自己的流转规则和业务逻辑)

final class PendingPaymentStatus extends OrderStatus {

public PendingPaymentStatus() {

super("PENDING", "待支付");

}

@Override

public boolean isTransitionAllowed(OrderStatus targetStatus) {

// 待支付可流转到已支付或已取消

return targetStatus instanceof PaidStatus || targetStatus instanceof CancelledStatus;

}

@Override

public void handleStatusChange(Order order) {

// 待支付状态逻辑:设置支付截止时间、发送提醒

order.setPaymentDeadline(LocalDateTime.now().plusHours(2));

notificationService.sendPaymentReminder(order.getUserId(), order.getOrderId());

}

}

final class PaidStatus extends OrderStatus {

public PaidStatus() {

super("PAID", "已支付");

}

@Override

public boolean isTransitionAllowed(OrderStatus targetStatus) {

// 已支付仅可流转到已完成

return targetStatus instanceof CompletedStatus;

}

@Override

public void handleStatusChange(Order order) {

// 已支付状态逻辑:记录支付时间、扣减库存

order.setPaidTime(LocalDateTime.now());

inventoryService.deductStock(order.getOrderItems());

}

}

final class CancelledStatus extends OrderStatus {

public CancelledStatus() {

super("CANCELLED", "已取消");

}

@Override

public boolean isTransitionAllowed(OrderStatus targetStatus) {

// 已取消不可流转

return false;

}

@Override

public void handleStatusChange(Order order) {

// 已取消状态逻辑:记录取消原因、恢复库存

order.setCancelledTime(LocalDateTime.now());

inventoryService.restoreStock(order.getOrderItems());

}

}

final class CompletedStatus extends OrderStatus {

public CompletedStatus() {

super("COMPLETED", "已完成");

}

@Override

public boolean isTransitionAllowed(OrderStatus targetStatus) {

// 已完成不可流转

return false;

}

@Override

public void handleStatusChange(Order order) {

// 已完成状态逻辑:记录完成时间、发送通知

order.setCompletedTime(LocalDateTime.now());

notificationService.sendCompletionNotice(order.getUserId(), order.getOrderId());

}

}

// 3. 使用密封类处理订单状态(结合模式匹配switch)

public void changeOrderStatus(Order order, OrderStatus targetStatus) {

OrderStatus currentStatus = order.getStatus();

// 判断流转是否合法

if (!currentStatus.isTransitionAllowed(targetStatus)) {

throw new IllegalArgumentException(

"非法状态流转:" + currentStatus.getStatusCode() + "→" + targetStatus.getStatusCode()

);

}

// 处理状态变更(编译期校验覆盖所有状态)

switch (targetStatus) {

case PendingPaymentStatus pendingStatus -> pendingStatus.handleStatusChange(order);

case PaidStatus paidStatus -> paidStatus.handleStatusChange(order);

case CancelledStatus cancelledStatus -> cancelledStatus.handleStatusChange(order);

case CompletedStatus completedStatus -> completedStatus.handleStatusChange(order);

}

// 更新订单状态

order.setStatus(targetStatus);

}

方案对比:
  • 传统枚举方案:状态逻辑集中在枚举类中,臃肿且难以扩展;新增状态需修改枚举类,违反 “开闭原则”;
  • 密封类方案:每个状态的逻辑封装在独立子类中,符合 “单一职责原则”;新增状态只需新增子类并在permits中添加,无需修改原有代码;模式匹配switch保证类型覆盖,无遗漏风险。

3.2 场景 2:支付方式管理(密封接口场景)

需求:管理支付方式,仅允许 “支付宝”“微信支付”“银联支付” 3 种方式,每种方式需实现不同的手续费计算逻辑和支付 API 调用逻辑;支持统一调用支付方法和计算手续费,无需关注具体支付方式。

传统方案(接口 +instanceof判断)
 

// 传统方案:接口定义支付方法,instanceof判断具体实现

public interface PaymentMethod {

BigDecimal calculateFee(BigDecimal amount);

boolean processPayment(Order order);

}

// 实现类:支付宝、微信支付、银联支付(无限制,可随意新增)

public class AlipayPayment implements PaymentMethod { /* 实现逻辑 */ }

public class WechatPayment implements PaymentMethod { /* 实现逻辑 */ }

public class UnionPayPayment implements PaymentMethod { /* 实现逻辑 */ }

public class CashPayment implements PaymentMethod { /* 错误实现,无手续费 */ }

// 使用接口处理支付(需instanceof判断,存在遗漏风险)

public void processOrderPayment(Order order, PaymentMethod paymentMethod) {

// 计算手续费(需instanceof判断具体类型)

BigDecimal fee;

if (paymentMethod instanceof AlipayPayment) {

fee = ((AlipayPayment) paymentMethod).calculateFee(order.getAmount());

} else if (paymentMethod instanceof WechatPayment) {

fee = ((WechatPayment) paymentMethod).calculateFee(order.getAmount());

} else if (paymentMethod instanceof UnionPayPayment) {

fee = ((UnionPayPayment) paymentMethod).calculateFee(order.getAmount());

} else {

throw new IllegalArgumentException("不支持的支付方式");

}

// 处理支付

boolean success = paymentMethod.processPayment(order);

if (success) {

order.setPaymentFee(fee);

order.setPaymentStatus("PAID");

} else {

throw new RuntimeException("支付失败");

}

}

密封接口方案(密封接口 + 模式匹配switch)
 

// 1. 定义密封接口PaymentMethod,指定3个实现类

sealed interface PaymentMethod permits AlipayPayment, WechatPayment, UnionPayPayment {

BigDecimal calculateFee(BigDecimal amount);

boolean processPayment(Order order);

}

// 2. 实现各支付方式(final,不可再继承)

final class AlipayPayment implements PaymentMethod {

@Override

public BigDecimal calculateFee(BigDecimal amount) {

// 支付宝手续费逻辑

return amount.multiply(new BigDecimal("0.006")).max(BigDecimal.ONE);

}

@Override

public boolean processPayment(Order order) {

// 调用支付宝API

return alipayClient.pay(order.getOrderId(), order.getAmount()).isSuccess();

}

}

final class WechatPayment implements PaymentMethod {

@Override

public BigDecimal calculateFee(BigDecimal amount) {

// 微信支付手续费逻辑

return amount.multiply(new BigDecimal("0.006"));

}

@Override

public boolean processPayment(Order order) {

// 调用微信支付API

return wxPayClient.pay(order.getOrderId(), order.getAmount()).isSuccess();

}

}

final class UnionPayPayment implements PaymentMethod {

@Override

public BigDecimal calculateFee(BigDecimal amount) {

// 银联支付手续费逻辑

return amount.multiply(new BigDecimal("0.005")).max(new BigDecimal("0.5"));

}

@Override

public boolean processPayment(Order order) {

// 调用银联支付API

return unionPayClient.pay(order.getOrderId(), order.getAmount()).isSuccess();

}

}

// 3. 使用密封接口处理支付(模式匹配switch,无遗漏风险)

public void processOrderPayment(Order order, PaymentMethod paymentMethod) {

// 计算手续费(编译期校验覆盖所有支付方式)

BigDecimal fee = switch (paymentMethod) {

case AlipayPayment alipay -> alipay.calculateFee(order.getAmount());

case WechatPayment wechat -> wechat.calculateFee(order.getAmount());

case UnionPayPayment unionPay -> unionPay.calculateFee(order.getAmount());

};

// 处理支付

boolean success = paymentMethod.processPayment(order);

if (success) {

order.setPaymentFee(fee);

order.setPaymentStatus("PAID");

} else {

throw new RuntimeException("支付失败:" + paymentMethod.getClass().getSimpleName());

}

}

方案对比:
  • 传统接口方案:接口实现类无限制,易出现非法实现;计算手续费需instanceof判断,存在遗漏风险;
  • 密封接口方案:仅允许指定的 3 个实现类,禁止非法实现;模式匹配switch保证覆盖所有支付方式,无遗漏风险;代码更简洁,无需手动类型转换。

3.3 场景 3:数据类型校验(密封类与记录类结合)

需求:校验用户输入的数据类型,支持 “字符串”“数字”“布尔值” 3 种类型,每种类型需实现不同的校验逻辑(如字符串长度校验、数字范围校验、布尔值格式校验);返回校验结果(成功 / 失败)和错误信息。

密封类 + 记录类方案(Java 16 + 记录类)
 

// 1. 定义密封类DataType,指定3个子类(使用记录类,简化代码)

sealed class DataType permits StringType, NumberType, BooleanType {

// 抽象方法:校验数据

public abstract ValidationResult validate(String input);

}

// 2. 实现各数据类型(使用记录类,自动生成构造器、getter、equals、hashCode)

// 字符串类型:需指定最小长度和最大长度

final record StringType(int minLength, int maxLength) extends DataType {

@Override

public ValidationResult validate(String input) {

if (input == null) {

return new ValidationResult(false, "字符串不能为空");

}

if (input.length() < minLength) {

return new ValidationResult(false, "字符串长度不能小于" + minLength);

}

if (input.length() > maxLength) {

return new ValidationResult(false, "字符串长度不能大于" + maxLength);

}

return new ValidationResult(true, "校验通过");

}

}

// 数字类型:需指定最小值和最大值

final record NumberType(double minValue, double maxValue) extends DataType {

@Override

public ValidationResult validate(String input) {

if (input == null) {

return new ValidationResult(false, "数字不能为空");

}

try {

double value = Double.parseDouble(input);

if (value < minValue) {

return new ValidationResult(false, "数字不能小于" + minValue);

}

if (value > maxValue) {

return new ValidationResult(false, "数字不能大于" + maxValue);

}

return new ValidationResult(true, "校验通过");

} catch (NumberFormatException e) {

return new ValidationResult(false, "输入不是有效的数字");

}

}

}

// 布尔值类型:仅允许"true"或"false"(不区分大小写)

final record BooleanType() extends DataType {

@Override

public ValidationResult validate(String input) {

if (input == null) {

return new ValidationResult(false, "布尔值不能为空");

}

if (!input.equalsIgnoreCase("true") && !input.equalsIgnoreCase("false")) {

return new ValidationResult(false, "布尔值必须是true或false");

}

return new ValidationResult(true, "校验通过");

}

}

// 3. 校验结果记录类

final record ValidationResult(boolean success, String message) {}

// 4. 使用密封类校验数据

public ValidationResult validateInput(String input, DataType dataType) {

// 模式匹配switch,覆盖所有数据类型

return switch (dataType) {

case StringType stringType -> stringType.validate(input);

case NumberType numberType -> numberType.validate(input);

case BooleanType booleanType -> booleanType.validate(input);

};

}

// 测试代码

public static void main(String[] args) {

// 校验字符串(长度2-10)

DataType stringType = new StringType(2, 10);

ValidationResult stringResult = validateInput("hello", stringType);

System.out.println("字符串校验:" + stringResult.success() + "," + stringResult.message()); // 成功

// 校验数字(1-100)

DataType numberType = new NumberType(1, 100);

ValidationResult numberResult = validateInput("123", numberType);

System.out.println("数字校验:" + numberResult.success() + "," + numberResult.message()); // 失败(超出范围)

// 校验布尔值

DataType booleanType = new BooleanType();

ValidationResult booleanResult = validateInput("TRUE", booleanType);

System.out.println("布尔值校验:" + booleanResult.success() + "," + booleanResult.message()); // </doubaocanvas>

http://www.dtcms.com/a/588901.html

相关文章:

  • 保健品网站模板wordpress简约主题分享
  • 前端低代码平台
  • 八字排盘原理
  • 40.交叉编译
  • RT-Thread Studio开发环境搭建
  • jdbc基础(连接篇)
  • 免费云服务器网站有哪些为什么手机进网站乱码
  • 从入门到精通 LlamaIndex RAG 应用开发
  • 算法基础篇:(五)基础算法之差分——以“空间”换“时间”
  • 潍坊中企动力做的网站怎么样wordpress显示摘要
  • leetcode1771.由子序列构造的最长回文串长度
  • 【JUnit实战3_31】第十九章:基于 JUnit 5 + Hibernate + Spring 的数据库单元测试
  • 双11释放新增量,淘宝闪购激活近场潜力
  • MySQL快速入门——内置函数
  • 中小网站建设都有哪些网易企业邮箱申请
  • 预测电流控制在光伏逆变器中的低延迟实现:华为FPGA加速方案与并网稳定性验证
  • C语言--文件读写函数的使用
  • 网站的网站维护的原因可以做公众号的网站
  • 使用waitpid回收多个子进程
  • leetcode1547.切棍子的最小成本
  • ThinkPHP8学习篇(十一):模型关联(一)
  • 深入理解Ribbon的架构原理
  • 力扣(LeetCode)100题:3.无重复字符的最长子串
  • 前端接口安全与性能优化实战
  • ssh网站怎么做wordpress搬家_后台错乱
  • LangChain V1.0 Messages 详细指南
  • 网站商城微信支付接口申请软件开发人工收费标准
  • 代码生成与开发辅助
  • claude code访问本地部署的MCP服务
  • 学习笔记8