模式匹配会引导你逐渐使用 sealed 关键字,这有助于确保你已覆盖了所有可能传入选择器表达式的类型。不过接下来再看一个示例:
SealedPatternMatch.java JDK 17
import java.util.List;
sealed interface Transport {
};
record Bicycle(String id) implements Transport {
};
record Glider(int size) implements Transport {
};
record Surfboard(double weight) implements Transport {
};
// If you uncomment this:
// record Skis(int length) implements Transport {};
// You get an error: "the switch expression
// does not cover all possible input values"
public class SealedPatternMatch {
static String exhaustive(Transport t) {
return switch (t) {
case Bicycle b -> "Bicycle " + b.id();
case Glider g -> "Glider " + g.size();
case Surfboard s -> "Surfboard " + s.weight();
};
}
public static void main(String[] args) {
List.of(
new Bicycle("Bob"),
new Glider(65),
new Surfboard(6.4)
).forEach(
t -> System.out.println(exhaustive(t))
);
try {
exhaustive(null); // 永远有可能发生! // [1]
} catch (NullPointerException e) {
System.out.println("Not exhaustive: " + e);
}
}
}
运行结果如下:
sealed interface Transport 是通过 record 实现的,record 自动就是 final 的。switch 覆盖了所有可能的 Transport 类型,而且如果增加一个新的类型,编译器就会检测出来,并告知你没有完全覆盖所有可能的模式。但是从[1]可以看出,仍然有一个case,编译器并未坚持让你覆盖:null。如果你没忘记加上 case null,便可以防止出现异常。但是编译器在此处帮不了你,显然是因为这会影响到太多已有的 switch 代码。
虽然模式匹配对于 Java 来说肯定是一项改进,但它也遭遇着和其他 Java 新增特性相同的问题:由于向后兼容性的缘故,它只能是相对更强大的 Scala 及 Kotlin 版本的某种不完全的实现。另外,由于匹配模式进入 Java 那缓慢而谨慎的步伐(这一点可以理解),可能需要过好几年才能看到完整版本的 Java 模式匹配。
理想情况下,Java 的模式匹配足以解决问题了。不过,如果你发现自己对模式匹配的依赖非常强烈,那么你可能需要一个比 Java 所提供的功能的更完整的功能集。Kotlin 语言有着更全面的模式匹配能力,而且能生成可以无缝放入现有 Java 程序的类文件。