重回Java怀抱:我发现的新特性

使用Java是后端工程师职业生涯中的经典时刻,最终你会发现自己打开IntelliJ(或Eclipse),面对~~XML文件~~Java类的美妙。我最后一次专业地使用Java是在经典的第8版发布时。之后,多家公司将后端开发从C#和Java转向Node.js,我也是如此。虽然环境不同,但我总是感觉自己在某种_Springboot精简版_中工作。看看这个inversify控制器的例子,你就会理解我的意思。
  1. @controller("/foo")
  2. export class FooController implements Controller {
  3. constructor(
  4. @inject("FooService")
  5. private fooService: FooService
  6. ) {}

  7. @httpGet("/")
  8. private index(
  9. @request() req: Request,
  10. @response() res: Response,
  11. @next() next: NextFunction): string {
  12. return this.fooService.get(req.query.id);
  13. }
  14. }
javascript
现在,新项目要求我再次用Java编程,我决定写一些文章来分享我(重新)学习Java的经验。
这篇文章讲述了在查看Java 17更新时真正引起我注意的两个特性:密封类和模式匹配。
Java 17:通过密封类重新获得控制权
Java 17引入了密封类(Sealed Classes),它允许你明确限制哪些其他类可以扩展或实现特定的类或接口。这给你对类层次结构更多的控制权,可以提高代码的清晰度、可维护性和安全性。例如,你可以定义一个PaymentMethod并指定它只能被定义的支付方法类扩展/实现,防止其他未授权的类继承它。这对于建模领域中有限的可能性集合变得非常有用。
  1. // 密封接口'PaymentMethod'只允许有限的实现集合
  2. public sealed interface PaymentMethod permits CreditCardPayment, PayPalPayment, BankTransferPayment {}

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

  7. public CreditCardPayment(String cardNumber, String cardHolderName) {
  8. this.cardNumber = cardNumber;
  9. this.cardHolderName = cardHolderName;
  10. }

  11. public String getCardNumber() {
  12. return cardNumber;
  13. }

  14. public String getCardHolderName() {
  15. return cardHolderName;
  16. }
  17. }

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

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

  24. public String getEmail() {
  25. return email;
  26. }
  27. }

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

  32. public BankTransferPayment(String accountNumber, String routingNumber) {
  33. this.accountNumber = accountNumber;
  34. this.routingNumber = routingNumber;
  35. }

  36. public String getAccountNumber() {
  37. return accountNumber;
  38. }

  39. public String getRoutingNumber() {
  40. return routingNumber;
  41. }
  42. }
java
这种设置保证你使用的任何PaymentMethod对象都必须是CreditCardPaymentPayPalPaymentBankTransferPayment之一。如果开发者试图创建一个实现PaymentMethod但不是三个允许类之一的新类,编译器会产生错误。虽然在小例子中这可能看起来不必要,但限制谁可以实现或扩展类的能力对于控制和管理代码库的增长非常有用。
Java 17:Switch的模式匹配
在Java 8中,如果你实现了一次策略模式,你可能见过这样的代码:
  1. public class PaymentProcessor {
  2. public void process(PaymentMethod paymentMethod) {
  3. if (payment instanceof CreditCardPayment) {
  4. System.out.println("Processing credit card payment for " + creditCard.getCardHolderName());
  5. // 处理信用卡的逻辑
  6. } else if (paymentMethod instanceof PayPalPayment) {
  7. System.out.println("Processing PayPal payment for " + payPal.getEmail());
  8. // 处理PayPal交易的逻辑
  9. } else if (paymentMethod instanceof BankTransferPayment) {
  10. System.out.println("Processing bank transfer to account " + bankTransfer.getAccountNumber());
  11. // 处理银行转账的逻辑
  12. } else {
  13. // 在这种情况下,我们需要添加一个"默认"处理器,因为
  14. // 编译器无法知道所有可能的方法
  15. throw new IllegalArgumentException("Unknown shape type");
  16. }
  17. }
  18. }
java
第17版为switch添加了模式匹配,允许在case标签中使用模式,使代码更加简洁和可读。这个特性消除了在instanceof检查后进行显式类型转换的需要,可以用来在单个switch语句中处理不同类型的对象。
密封接口与switch的模式匹配结合时极其强大。你可以编写你的逻辑而无需if-else链的样板代码。
  1. public class PaymentProcessor {
  2. public void process(PaymentMethod paymentMethod) {
  3. switch (paymentMethod) {
  4. case CreditCardPayment creditCard -> {
  5. System.out.println("Processing credit card payment for " + creditCard.getCardHolderName());
  6. // 处理信用卡的逻辑
  7. }
  8. case PayPalPayment payPal -> {
  9. System.out.println("Processing PayPal payment for " + payPal.getEmail());
  10. // 处理PayPal交易的逻辑
  11. }
  12. case BankTransferPayment bankTransfer -> {
  13. System.out.println("Processing bank transfer to account " + bankTransfer.getAccountNumber());
  14. // 处理银行转账的逻辑
  15. }
  16. // 不需要'default'情况,因为编译器知道所有
  17. // 可能的'PaymentMethod'子类都已经处理了
  18. }
  19. }
  20. }
java
因为PaymentMethod接口是密封的,Java编译器可以分析switch语句并验证所有可能的PaymentMethod类型都被覆盖了。这提供了编译时安全性,防止了在引入新的、未处理的支付类型时可能发生的bug。编译器本质上保证你的代码处理每个有效的场景。
总结
Java 17的这两个特性——密封类和switch的模式匹配——为Java带来了更强的类型安全性和更简洁的代码表达。密封类让我们可以精确控制类层次结构,而模式匹配则让处理不同类型的对象变得更加优雅。这些特性的结合使得Java在现代编程语言的竞争中重新焕发活力,为开发者提供了既安全又表达力强的编程体验。