提到模式匹配(PatternMatching),Java开发人员可能会比较陌生。实际上,其他编程语言的开发人员早就已经使用过模式匹配了。JVM上的编程语言Scala的模式匹配功能就很强大。什么是模式匹配? 为了更好地解释模式匹配,我们从一个简单的例子开始。我们希望创建一个方法,可以把任何对象转换成String格式。这就需要根据对象的类型来进行不同的格式化操作。我们可以很容易就写出下面这样的代码。这段代码的核心是使用instanceof操作符来检查输入对象的类型,再根据对象类型进行格式化操作。publicclassObjectFormatter{publicStringformat(Objectinput){if(inputnull){}elseif(inputinstanceofNumber){returnNumberFormat。getNumberInstance()。format(input);}elseif(inputinstanceofLocalDateTime){return((LocalDateTime)input)。format(DateTimeFormatter。ISODATETIME);}else{returninput。toString();}}} 上述对instanceof操作符的使用就是模式匹配的一种简单形式。 一个模式由匹配predicate和模式变量的集合组成。匹配predicate判断一个模式是否可以匹配目标对象。如果模式匹配的话,模式变量的集合用来从目标对象中提取值。 在instanceof操作符的例子中,匹配predicate的作用是检查目标对象的类型,而模式变量的集合中只有一个变量,就是目标对象自身。这种类型的模式,被称为类型模式(typepattern)。除了类型模式之外,计划中的模式还包括记录类型模式和数组模式。 模式匹配是一个涵盖范围非常大的功能。根据现在Java的发布周期,模式匹配的内容会在不同的Java版本中逐渐添加进来。具体的发布周期可以参考下面的表格。这个表格的右侧三列表示的是不同的与模式匹配相关的功能,每一行表示这些功能在对应Java版本中的可用状态。 Java版本 instanceof模式 switch的模式匹配 记录类型模式 Java14 预览 Java15 二次预览 Java16 正式功能 Java17 正式功能 预览 Java18 正式功能 二次预览 Java19 正式功能 三次预览 预览 以Java17为例,可以使用instance模式的正式功能,以及switch模式匹配的预览功能。 Java18和Java19中可用的模式匹配功能也列在了表格中,作为参考。instanceof模式匹配 Java中的instanceof操作符用来检查对象的类型。下面的代码给出了通常使用instanceof操作符的代码范式。在if语句中使用instanceof来进行检查,如果检查通过,则使用强制类型转换,把输入对象obj转换成String类型的s,最后再使用变量s。if(objinstanceofString){Strings(String)} 从上述代码中可以看到,对instanceof操作符的使用范式是非常繁琐的,其中需要检查的目标类型String就出现了三次。在使用了instanceof模式匹配之后,代码可以简化很多。在下面的代码中,Strings表示类型模式,其中String是需要匹配的类型,s是匹配成功之后用来捕获目标对象的变量。该变量s可以直接在if语句块中使用。if(objinstanceofStrings){System。out。println(s。toUpperCase());} 模式变量使用的是流作用域(flowscoping)。一个模式变量能够出现在作用域中,当且仅当编译器可以推断出模式匹配必定成功,并且该变量被赋予了一个值时。在上面的例子中,if语句块的代码只有在模式匹配成功了之后才会执行,变量s此时必定被赋予了值obj,因此编译器可以确定s必定在if语句块的作用域中。 关于流作用域,其实不用了解太多。如果使用错误,编译器会提示你的。 下面的代码给出了instanceof模式匹配的代码示例。第一个if匹配String类型的同时,加上了对字符串长度的检查;第二个if匹配剩下的String类型的对象。在第一个if的条件中,objinstanceofStrings和s。length()10的顺序不能反过来。这里利用了的短路(shortcircuit)特性,当第一个instanceof模式匹配成功之后,才会执行后面的判断,这个时候s必然是一个String对象,可以安全地使用length方法;如果第一个instanceof模式不匹配,后面的判断不会被执行,因此也不会出现错误。publicclassStringMatch{publicvoidtest(Objectobj){if(objinstanceofStringss。length()10){System。out。println(长字符串s);}elseif(objinstanceofStrings){System。out。println(短字符串s);}else{System。out。println(其他);}}}在switch语句和表达式中使用模式匹配 在Java17中,switch语句和表达式的case子句中可以使用模式匹配。该功能在Java17中是预览功能,因此需要通过命令行参数enablepreview来启用。switch在很多时候可以替代嵌套的ifelse。 下面的代码使用switch语句加上模式匹配改写了上面的使用嵌套ifelse的代码示例。使用switch比ifelse更加简洁。这里的switch用的是箭头格式。publicclassStringMatch{publicvoidtest(Objectobj){switch(obj){caseStringss。length()10System。out。println(长字符串s);caseStringsSystem。out。println(短字符串s);defaultSystem。out。println(其他);}}} 我们可以用switch语句改写文章开头提到的对象格式化的方法,如下面的代码所示。使用switch语句加上模式匹配的代码更加简洁易懂。publicclassObjectFormatter{publicStringformat(Objectinput){returnswitch(input){caseNumbernNumberFormat。getNumberInstance()。format(n);caseLocalDateTimett。format(DateTimeFormatter。ISODATETIME);defaultinput。toString();};}}