JDK 16 最终完成了 JEP (Java增强建议)394的定稿,但它有个糟糕的名字,即“针对 instanceof 的模式匹配”(Pattern Matching for instanceof)。如果你看到了完整的模式匹配,便会明白这个名字不是很合理。称其为“模式匹配支持”(support for pattern matching)可能会更恰当。在 Kotlin 语言中,该特性被简单地称为“智能转型”(smart casting),因为一旦确定类型后,就永远不需要对其转型了。
下面的示例中可以看到如何分别用旧方法和新方法实现同样的目的:
SmartCasting.java
public class SmartCasting {
static void dumb(Object x) {
if (x instanceof String) {
String s = (String) x;
if (s.length() > 0) {
System.out.format("%d %s%n", s.length(), s.toUpperCase());
}
}
}
static void smart(Object x) {
if (x instanceof String s && s.length() > 0) {
System.out.format("%d %s%n", s.length(), s.toUpperCase());
}
}
static void wrong(Object x) {
// “||”永远不会生效:
// if(x instanceof String s || s.length() > 0) {}
// error: cannot find symbol ^
}
public static void main(String[] args) {
dumb("dumb");
smart("smart");
}
}
运行结果如下:
在 dumb() 中,一旦 instanceof 确定了 x 是 String 后,就必须显式地将它转型为 String s。否则就得在该函数中其余的地方到处插入转型操作。但是在 smart() 中,注意 x instanceof String s 自动用String 类型创建了一个新的变量 s。s 在整个作用域中都可用,即使是在剩余的 if 条件中,如 && s.length() > 0 中所示。这样可以得到更紧凑的代码。
从 wrong() 可以看出,在 if 智能转型表达式中只能使用 &&。使用 || 则意味着可能 x 是个 instanceof String,也可能 s.length() > 0。但这也就意味着 x 也可能不是String,在这种情况下,Java就不会将x 智能转型以生成 s,因此 s 在 || 右侧是不可用的。
JEP 394 将 s 称为模式变量(pattern variable)。
虽然这个特性并未完全消除某些混乱的if语句,但这并不是加入该特性的目的。该特性是作为模式匹配的构建块而引入的,你很快就会看到。
该特性可以产生某些奇怪的作用域行为:
OddScoping.java
public class OddScoping {
static void f(Object o) {
if (!(o instanceof String s)) {
System.out.println("Not a String");
throw new RuntimeException();
}
//此处s仍在作用戏中!
System.out.println(s.toUpperCase()); // [1]
}
public static void main(String[] args) {
f("Curiouser and Curiouser");
f(null);
}
}
[1] 只有在不引入会引发异常的语句时,s 才在作用域中。如果注释掉 throw new RuntimeException(),编译器便会告诉你它无法在行 [1] 中找到 s ——这是你通常所预期的行为。
乍一看这像个 bug,但它就是这么设计的——该行为在 JEP 394 中有着明确的描述。尽管这可以说是一个极端情况,但你能想象追踪由这种行为引起的 bug 会有多么困难。