Java 14 记录和数组 List< Integer >解决方法

2022-09-02 19:36:11

给定以下代码:

public static void main(String[] args) {
    record Foo(int[] ints){}

    var ints = new int[]{1, 2};
    var foo = new Foo(ints);
    System.out.println(foo); // Foo[ints=[I@6433a2]
    System.out.println(new Foo(new int[]{1,2}).equals(new Foo(new int[]{1,2}))); // false
    System.out.println(new Foo(ints).equals(new Foo(ints))); //true
    System.out.println(foo.equals(foo)); // true
}

显然,似乎使用了数组的方法(而不是静态方法,或)。toStringequalsArrays::equalsArrays::deepEqualsArray::toString

所以我想Java 14 Records(JEP 359)不能很好地与数组一起使用,相应的方法必须使用IDE生成(至少在IntelliJ中,默认情况下生成“有用”的方法,即它们使用中的静态方法)。Arrays

还是有其他解决方案?


答案 1

Java数组给记录带来了一些挑战,这些挑战给设计增加了许多约束。数组是可变的,它们的相等语义(从 Object 继承)是通过标识而不是内容。

您的示例的基本问题是,您希望数组意味着内容相等,而不是引用相等。记录的(默认)语义基于组件的相等性;在您的示例中,包含不同数组的两条记录是不同的,并且记录的行为正确。问题是你只是希望平等比较是不同的。equals()equals()Foo

也就是说,你可以用你想要的语义声明一个记录,它只是需要更多的工作,你可能会觉得工作量太大了。下面是执行所需操作的记录:

record Foo(String[] ss) {
    Foo { ss = ss.clone(); }
    String[] ss() { return ss.clone(); }
    public boolean equals(Object o) { 
        return o instanceof Foo f && Arrays.equals(f.ss, ss);
    }
    public int hashCode() { return Objects.hash(Arrays.hashCode(ss)); }
}

这样做是在进入(在构造函数中)和在退出(在访问器中)的途中的防御性副本,以及调整相等语义以使用数组的内容。这支持超类中必需的不变量,即“将记录分解为其组件,并将组件重建为新记录,从而产生相等的记录。java.lang.Record

你可能会说:“但工作量太大了,我想使用记录,这样我就不必输入所有这些东西。但是,记录主要不是一种语法工具(尽管它们在语法上更令人愉快),它们是一种语义工具:记录是名义元组。大多数时候,紧凑的语法也会产生所需的语义,但是如果你想要不同的语义,你必须做一些额外的工作。


答案 2

List< Integer >解决方法

解决办法:使用整数对象列表 () 而不是基元数组 ()。List< Integer >int[]

在此示例中,我使用添加到 Java 9 的 List.of 功能实例化了未指定类的不可修改列表。您也可以将ArrayList用于由数组支持的可修改列表。

package work.basil.example;

import java.util.List;

public class RecordsDemo
{
    public static void main ( String[] args )
    {
        RecordsDemo app = new RecordsDemo();
        app.doIt();
    }

    private void doIt ( )
    {

        record Foo(List < Integer >integers)
        {
        }

        List< Integer > integers = List.of( 1 , 2 );
        var foo = new Foo( integers );

        System.out.println( foo ); // Foo[integers=[1, 2]]
        System.out.println( new Foo( List.of( 1 , 2 ) ).equals( new Foo( List.of( 1 , 2 ) ) ) ); // true
        System.out.println( new Foo( integers ).equals( new Foo( integers ) ) ); // true
        System.out.println( foo.equals( foo ) ); // true
    }
}