提到模式匹配,可能很多 Java 开发人员比较陌生。
了解模式匹配
我们从一个具体的示例出发来看模式匹配的作用。我们的目标是把任意的 Java 对象转换成字符串。我们可以很容易的写出类似下面的代码,也就是 ObjectFormatter
类。
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
操作符检查参数对象的类型,再执行相应的操作。对 instanceof
操作符的使用,实际上就是模式匹配的一种简单形式。
模式匹配中的模式由两个部分组成,分别是匹配 predicate 和模式变量。匹配 predicate 判断一个模式是否与目标相匹配。如果模式匹配成功,模式变量表示从目标中提取的值。
以 instanceof
操作符来说,匹配 predicate 是检查目标的类型。当匹配成功之后,模式变量只有一个,那就是该对象本身。这种模式称为类型模式。除此之外,还有记录模式和数组模式。
模式匹配是一个很大的功能,会在多个 Java 版本中逐步加入。以 instanceof
操作符的模式匹配来说,在 Java 14 中以预览功能的形式引入,在 Java 15 中再次预览,在 Java 16 中成为正式功能。switch
语句对模式匹配的支持,在 Java 17 中首次预览。记录模式和数组模式,则计划在 Java 18 中首次预览。
instanceof 模式匹配
下面介绍 instanceof
的模式匹配。相信很多人都写过下面这样的代码。在这段代码中,String
出现了3次,代码比较繁琐。
if (obj instanceof String) {
String s = (String) obj;
}
在使用了模式匹配之后,代码可以简化为如下所示。
if (obj instanceof String s) {
System.out.println(s.toUpperCase());
}
String s
称为类型模式,其中 String
是进行匹配的 predicate,而 s
是匹配成功之后的变量。s
的类型就已经是 String
了,不需要进行类型转换。
模式变量使用的是流作用域。只有当编译器可以推断出模式被匹配成功,并且变量被赋值之后,该变量才会出现在作用域中。在上面的代码中,if
中的代码只有在模式匹配成功之后才会执行,因此变量 s
必定在作用域中,可以安全使用。
在模式匹配时,可以添加额外的守卫条件来限制匹配的条件。在下面的代码中,第一个模式匹配长度大于 10
的字符串,第二个模式匹配其他的字符串。
public class StringMatch {
public void test(Object obj) {
if (obj instanceof String s && s.length() > 10) {
System.out.println("long string -> " + s);
} else if (obj instanceof String s) {
System.out.println("short string -> " + s);
} else {
System.out.println("others");
}
}
}
switch 的模式匹配
在 switch
语句中,也可以使用模式匹配。如下面的代码所示。可以看到相关的代码非常简洁。
public class StringMatch {
public void test(Object obj) {
switch (obj) {
case String s && s.length() > 10 -> System.out.println(
"long string -> " + s);
case String s -> System.out.println("short string -> " + s);
default -> System.out.println("others");
}
}
}
需要注意的是,switch
语句的模式匹配在 Java 17 中是预览功能,在编译和运行时都必须添加选项 --enable-preview
来启用。
最后我们得到了 ObjectFormatter
的简化实现,代码的可读性更好。
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();
};
}
}
关于模式匹配的内容就介绍到这里,相应的视频见我的B站。
推一下我的新书《Quarkus云原生微服务开发实战》,京东淘宝上搜索书名即可。