instanceof 中的模式匹配有时无法解析理论上可解析的范围

Pattern matching in instanceof sometimes cannot resolve theoretically resolvable scope

import org.w3c.dom.Comment;
import org.w3c.dom.Document;
import org.w3c.dom.Node;

List<Node> list = new ArrayList<>();
Node x = list.get(0);
if (!(x instanceof Comment comment)) {
    if (!(x instanceof Document doc && doc.getFirstChild() instanceof Comment comment)) {
        return null;
    }
}
System.out.println(comment.ToString()); // cannot resolve comment

变量 comment 无法解析,但它应该是 - 我们涵盖了 2 个互斥的快乐路径,每个路径都分配 Comment comment - 系统输出应该能够看到 comment , 否则 return null 会被触发。

您不能使用模式匹配在两个不同的地方声明同一个变量。这些仍然是两个不同的变量,具有两个不同的范围。

或许,我们可以通过给它们不同的名字来消除混淆:

if (!(x instanceof Comment comment)) {
    if (!(x instanceof Document doc && doc.getFirstChild() instanceof Comment comment2)) {
        return null;
    }
    //comment2 and doc are in scope here,
    //but their scope ends with an enclosing block
    System.out.println(comment2.toString());
}
//FAIL! You can't access comment2 outside its scope
System.out.println(comment2.toString()); 
//FAIL! You can't access comment here because 
//the compiler can't guarantee that the block above exited.
System.out.prinltn(comment.toString()

您可以使用如下简单示例验证模式匹配变量是否仍保留在其封闭范围内:

{
    Object o = null;
    if(!(o instanceof String s)) {
          return;
    }
    System.out.println(s); //OK
}
System.out.println(s); //Doesn't work

抱歉,我确实没有可供您使用的优雅替代品。你可以通过一个有点丑陋的解决方法来实现你想要的:

Comment comment;
if (!(x instanceof Comment c)) {
    if (!(x instanceof Document doc && doc.getFirstChild() instanceof Comment c)) {
        return null;
    }
    comment = c;
} else {
    comment = c;
}
System.out.println(comment.toString());

如果您可以将此代码段移至单独的方法中,则此解决方法会变得更好:

private static Comment extractComment(Node x) {
    if (!(x instanceof Comment comment)) {
        if (!(x instanceof Document doc && doc.getFirstChild() instanceof Comment comment)) {
            return null;
        }
        return comment;
    } 
    return comment;
}

甚至:

private static Comment extractComment(Node x) {
    if (x instanceof Comment comment) {
        return comment;
    }     
    if (x instanceof Document doc && doc.getFirstChild() instanceof Comment comment) {
        return comment;
    }
    return null;
}