Java 17新特性解析:密封类与模式匹配的完美结合

本文详细介绍了Java 17引入的两大重要特性:密封类和模式匹配。通过支付系统的实际代码示例,展示了如何用密封接口限制类继承,以及如何利用模式匹配简化switch语句,提升代码安全性和可读性。文章包含完整的代码演示,适合Java开发者学习新版本特性。

重回Java的发现(截至目前)

使用Java是后端工程师职业生涯中的经典时刻,最终你都会遇到IntelliJ(或Eclipse)。我上次认真使用Java还是在经典的版本8时代。现在,新项目要求我使用Java 17和21进行编码。我决定写一些文章来分享我(重新)学习Java的经验。本文重点介绍我在查看Java 17更新时真正吸引我注意的两个特性:密封类和模式匹配。

Java 17:用密封类重获控制权

Java 17引入了密封类,允许你显式限制哪些其他类可以扩展或实现特定的类或接口。这让你对类层次结构有更多控制,可以提高代码的清晰度、可维护性和安全性。例如,你可以定义一个PaymentMethod,并指定它只能由定义的支付方法类扩展/实现,防止其他未经授权的类继承它。这在为领域中的有限可能性集建模时变得非常有用。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// 密封接口'PaymentMethod'允许有限的实现集
public sealed interface PaymentMethod permits CreditCardPayment, PayPalPayment, BankTransferPayment {}

// 信用卡支付的final类
public final class CreditCardPayment implements PaymentMethod {
  private final String cardNumber;
  private final String cardHolderName;

  public CreditCardPayment(String cardNumber, String cardHolderName) {
    this.cardNumber = cardNumber;
    this.cardHolderName = cardHolderName;
  }

  public String getCardNumber() {
    return cardNumber;
  }

  public String getCardHolderName() {
    return cardHolderName;
  }
}

// PayPal支付的final类
public final class PayPalPayment implements PaymentMethod {
  private final String email;

  public PayPalPayment(String email) {
    this.email = email;
  }

  public String getEmail() {
    return email;
  }
}

// 银行转账支付的final类
public final class BankTransferPayment implements PaymentMethod {
  private final String accountNumber;
  private final String routingNumber;

  public BankTransferPayment(String accountNumber, String routingNumber) {
    this.accountNumber = accountNumber;
    this.routingNumber = routingNumber;
  }

  public String getAccountNumber() {
    return accountNumber;
  }

  public String getRoutingNumber() {
    return routingNumber;
  }
}

这种设置保证了你处理的任何PaymentMethod对象都必须是CreditCardPayment、PayPalPayment或BankTransferPayment之一。如果开发人员尝试创建一个实现PaymentMethod的新类,但不是三个允许的类之一,编译器将产生错误。虽然在小例子中这可能看起来不必要,但限制谁可以实现或扩展类的能力对于控制和管理代码库的增长非常有用。

Java 17:switch的模式匹配

在Java 8中,如果你曾经实现过策略模式,可能见过这样的代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
public class PaymentProcessor {
  public void process(PaymentMethod paymentMethod) {
    if (paymentMethod instanceof CreditCardPayment) {
      CreditCardPayment creditCard = (CreditCardPayment) paymentMethod;
      System.out.println("Processing credit card payment for " + creditCard.getCardHolderName());
      // 处理信用卡的逻辑
    } else if (paymentMethod instanceof PayPalPayment) {
      PayPalPayment payPal = (PayPalPayment) paymentMethod;
      System.out.println("Processing PayPal payment for " + payPal.getEmail());
      // 处理PayPal交易的逻辑
    } else if (paymentMethod instanceof BankTransferPayment) {
      BankTransferPayment bankTransfer = (BankTransferPayment) paymentMethod;
      System.out.println("Processing bank transfer to account " + bankTransfer.getAccountNumber());
      // 处理银行转账的逻辑
    } else {
      // 这种情况下我们需要添加一个"默认"处理器
      // 因为编译器无法知道所有可能的方法
      throw new IllegalArgumentException("Unknown shape type");
    }
  }
}

版本17增加了switch的模式匹配,允许在case标签中使用模式,使代码更简洁易读。该特性消除了在instanceof检查后需要显式类型转换的需求,并可用于在单个switch语句中处理不同类型的对象。

密封接口与switch的模式匹配结合时极其强大。你可以编写干净、简洁的逻辑来处理每种支付类型,而无需if-else链的样板代码。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
public class PaymentProcessor {
  public void process(PaymentMethod paymentMethod) {
    switch (paymentMethod) {
      case CreditCardPayment creditCard -> {
        System.out.println("Processing credit card payment for " + creditCard.getCardHolderName());
        // 处理信用卡的逻辑
      }
      case PayPalPayment payPal -> {
        System.out.println("Processing PayPal payment for " + payPal.getEmail());
        // 处理PayPal交易的逻辑
      }
      case BankTransferPayment bankTransfer -> {
        System.out.println("Processing bank transfer to account " + bankTransfer.getAccountNumber());
        // 处理银行转账的逻辑
      }
      // 不需要'default'情况,因为编译器知道所有
      // 'PaymentMethod'的可能子类都已处理
    }
  }
}

由于PaymentMethod接口是密封的,Java编译器可以分析switch语句并验证所有可能的PaymentMethod类型都已覆盖。这提供了编译时安全性,防止在引入新的未处理支付类型时可能出现的错误。编译器基本上保证你的代码处理了每个有效场景。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计