为什么空值与记录模式匹配?

Rob*_*uez 1 java pattern-matching record-patterns

我一直在尝试使用 Java 21 中的记录模式进行模式匹配的示例。官方文档断言空值与任何记录模式都不匹配。但是,我尝试这个例子:

record Point(Integer x, Integer y) {}

public class MainPatternMatchingRecord {
    public static void main(String[] args) {
        printSum(new Point(null, 2));
    }

    private static void printSum(Object obj) {
        if (obj instanceof Point(var x, var y)) {
            System.out.println(x + 1);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在这里,根据我对 JEP 的理解,new Point(null, 2)不应该匹配 in instanceof Point(var x, var y),但是当运行程序时,会抛出此异常:

线程“main”中的异常 java.lang.NullPointerException:无法调用“java.lang.Integer.intValue()”,因为“x”为 null

为什么会出现这种行为?应该如何正确解释空值与任何记录模式不匹配?

Swe*_*per 6

JEP 440确实说:

空值与任何记录模式都不匹配。

但仔细看看记录模式是什么:

记录模式由记录类类型和(可能为空)模式列表组成 [...]

JEP 还指定了记录模式的语法,如下所示:

Pattern:
  TypePattern
  RecordPattern

TypePattern:
  LocalVariableDeclaration

RecordPattern:
  ReferenceType ( [ PatternList ] )

PatternList : 
  Pattern { , Pattern }
Run Code Online (Sandbox Code Playgroud)

通过这些定义,我们可以看出var xvar y不是记录模式。它们是类型模式。“空值与任何记录模式都不匹配”的规则不适用于var xvar y

该规则确实适用于整个Point(var x, var y)模式,即记录模式,因此如果obj为 null,则不会匹配Point(var x, var y)

我无法在 JEP 中准确找到记录模式中的类型模式的行为方式,但预览规范确实明确指出了这一点:

例如考虑:

class Super {}
class Sub extends Super {}
record R(Super s) {}
Run Code Online (Sandbox Code Playgroud)

我们期望类型的所有非空值都R与模式匹配 R(Super s),包括评估表达式所产生的值new R(null)。(即使空值与模式不匹配Super s。)但是,我们不希望该值与模式匹配R(Sub s),因为null记录组件的值与模式不匹配Sub s

然后,根据记录声明确定出现在嵌套模式列表中的模式的含义。解析会替换出现在嵌套模式列表中的任何类型模式,该列表应null与所有值(包括特殊的 any 模式的实例)匹配。在上面的示例中,模式R(Sub s)被解析为pattern R(Sub s),而模式R(Super s)被解析为具有类型的记录模式R和包含any模式的嵌套模式列表。

换句话说,Point(var x, var y)就是决心Point(<any>, <any>)。这里的模式<any>可以匹配所有内容,包括 null。


这是另一个例子:

record Foo(Integer x, Integer y) {}

record Bar(Foo a, Foo b) {}
Run Code Online (Sandbox Code Playgroud)
private static void printSum(Object obj) {
    if (obj instanceof Bar(Foo(Integer x1, Integer y1), Foo(var x2, var y2))) {
        System.out.println("matched!");
    }
}
Run Code Online (Sandbox Code Playgroud)

现在, anew Bar(null, null)将与模式Bar(Foo(Integer x1, Integer y1), Foo(var x2, var y2))匹配,因为Foo(Integer x1, Integer y1)是一个记录模式,并且null与它不匹配。