详解 Java 17 中的模式匹配(Pattern Matching)

提到模式匹配(Pattern Matching),Java 开发人员可能会比较陌生。实际上,其他编程语言的开发人员早就已经使用过模式匹配了。JVM 上的编程语言 Scala 的模式匹配功能就很强大。

什么是模式匹配?

为了更好地解释模式匹配,我们从一个简单的例子开始。我们希望创建一个方法,可以把任何对象转换成 String 格式。这就需要根据对象的类型来进行不同的格式化操作。我们可以很容易就写出下面这样的代码。这段代码的核心是使用 instanceof 操作符来检查输入对象的类型,再根据对象类型进行格式化操作。

public class ObjectFormatter {

  public String format(Object input) {
    if (input == null) {
      return "";
    } else if (input instanceof Number) {
      return NumberFormat.getNumberInstance().format(input);
    } else if (input instanceof LocalDateTime) {
      return ((LocalDateTime) input).format(DateTimeFormatter.ISO_DATE_TIME);
    } else {
      return input.toString();
    }
  }
}

上述对 instanceof 操作符的使用就是模式匹配的一种简单形式。

一个模式由匹配 predicate 和模式变量的集合组成

在 instanceof 操作符的例子中,匹配 predicate 的作用是检查目标对象的类型,而模式变量的集合中只有一个变量,就是目标对象自身。这种类型的模式,被称为类型模式(type pattern)。除了类型模式之外,计划中的模式还包括记录类型模式和数组模式。

模式匹配是一个涵盖范围非常大的功能。根据现在 Java 的发布周期,模式匹配的内容会在不同的 Java 版本中逐渐添加进来。具体的发布周期可以参考下面的表格。这个表格的右侧三列表示的是不同的与模式匹配相关的功能,每一行表示这些功能在对应 Java 版本中的可用状态。

Java版本

instanceof 模式

switch 的模式匹配

记录类型模式

Java 14

预览



Java 15

二次预览



Java 16

正式功能



Java 17

正式功能

预览


Java 18

正式功能

二次预览


Java 19

正式功能

三次预览

预览

以 Java 17 为例,可以使用 instance 模式的正式功能,以及 switch 模式匹配的预览功能。

Java 18 和 Java 19 中可用的模式匹配功能也列在了表格中,作为参考。

instanceof 模式匹配

Java 中的 instanceof 操作符用来检查对象的类型。下面的代码给出了通常使用 instanceof 操作符的代码范式。在 if 语句中使用 instanceof 来进行检查,如果检查通过,则使用强制类型转换,把输入对象 obj 转换成 String 类型的 s,最后再使用变量 s。

if (obj instanceof String) {
  String s = (String) obj;
}

从上述代码中可以看到,对 instanceof 操作符的使用范式是非常繁琐的,其中需要检查的目标类型 String 就出现了三次。在使用了 instanceof 模式匹配之后,代码可以简化很多。在下面的代码中, String s 表示类型模式,其中 String 是需要匹配的类型,s 是匹配成功之后用来捕获目标对象的变量。该变量 s 可以直接在 if 语句块中使用。

if (obj instanceof String s) {
  System.out.println(s.toUpperCase());
}

模式变量使用的是流作用域(flow scoping)。一个模式变量能够出现在作用域中,当且仅当编译器可以推断出模式匹配必定成功,并且该变量被赋予了一个值时。在上面的例子中,if 语句块的代码只有在模式匹配成功了之后才会执行,变量 s 此时必定被赋予了值 obj,因此编译器可以确定 s 必定在 if 语句块的作用域中。

关于流作用域,其实不用了解太多。如果使用错误,编译器会提示你的。

下面的代码给出了 instanceof 模式匹配的代码示例。第一个 if 匹配 String 类型的同时,加上了对字符串长度的检查;第二个 if 匹配剩下的 String 类型的对象。在第一个 if 的条件中,obj instanceof String s 和 s.length() > 10 的顺序不能反过来。这里利用了 && 的短路(short-circuit)特性,当第一个 instanceof 模式匹配成功之后,才会执行后面的判断,这个时候 s 必然是一个 String 对象,可以安全地使用 length 方法;如果第一个 instanceof 模式不匹配,后面的判断不会被执行,因此也不会出现错误。

public class StringMatch {

  public void test(Object obj) {
    if (obj instanceof String s && s.length() > 10) {
      System.out.println("长字符串 -> " + s);
    } else if (obj instanceof String s) {
      System.out.println("短字符串 -> " + s);
    } else {
      System.out.println("其他");
    }
  }
}

在 switch 语句和表达式中使用模式匹配

在 Java 17 中,switch 语句和表达式的 case 子句中可以使用模式匹配。该功能在 Java 17 中是预览功能,因此需要通过命令行参数 --enable-preview 来启用。switch 在很多时候可以替代嵌套的 if/else。

下面的代码使用 switch 语句加上模式匹配改写了上面的使用嵌套 if/else 的代码示例。使用 switch 比 if/else 更加简洁。这里的 switch 用的是箭头格式。

public class StringMatch {

  public void test(Object obj) {
    switch (obj) {
      case String s && s.length() > 10 -> System.out.println(
          "长字符串 -> " + s);
      case String s -> System.out.println("短字符串 -> " + s);
      default -> System.out.println("其他");
    }
  }
}

我们可以用 switch 语句改写文章开头提到的对象格式化的方法,如下面的代码所示。使用 switch 语句加上模式匹配的代码更加简洁易懂。

public class ObjectFormatter {

  public String format(Object input) {
    return switch (input) {
      case null -> "";
      case Number n -> NumberFormat.getNumberInstance().format(n);
      case LocalDateTime t -> t.format(DateTimeFormatter.ISO_DATE_TIME);
      default -> input.toString();
    };
  }
}
展开阅读全文

页面更新:2024-04-12

标签:模式   字符串   变量   语句   详解   对象   目标   类型   操作   代码   功能

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2020-2024 All Rights Reserved. Powered By 71396.com 闽ICP备11008920号-4
闽公网安备35020302034903号

Top