27

假设以下示例:

public record SomeRecord(int foo, byte bar, long baz)
{ }

我是否需要覆盖hashCodeequals如果我要将所述对象添加到 a 中HashMap

4

3 回答 3

28

不,您不需要定义自己的hashCodeand equals。如果您希望覆盖默认实现,您可以这样做。

有关详细信息,请参阅规范的第 8.10.3 节https://docs.oracle.com/javase/specs/jls/se14/preview/specs/records-jls.html#jls-8.10

请特别注意,实现您自己的这些版本的警告:

从 java.lang.Record 继承的所有成员。除非在记录体中显式重写,否则 R 已经隐式声明了重写 java.lang.Record 中的 equals、hashCode 和 toString 方法的方法。

如果 java.lang.Record 中的任何这些方法在记录主体中显式声明,则实现应满足 java.lang.Record 中指定的预期语义。

特别是,自定义equals实现必须满足记录副本必须等于记录的预期语义。对于类来说这通常不是真的(例如,如果两个对象的值相同,即使字段不同,它们Car也可能是这样),但对于记录来说必须是真的。这个限制意味着很少有任何理由覆盖.equalsVINownerequals

于 2020-05-10T22:52:45.093 回答
5

您是否需要它的答案实际上是 -这取决于您决定创建为Record. 在编译或运行时也没有任何限制来约束你这样做,而且Object无论如何扩展的类都是如此。

另一方面,该提案的主要动机之一是“低价值、重复、容易出错的代码:构造函数、访问器equals()hashCode()...toString()”。在数据载体中,这在当今的 Java 编程中很常见。因此,进一步陈述的决定是更喜欢语义目标

...:将数据建模为数据。(如果语义正确,样板将自行处理。)声明浅不可变、行为良好的名义数据聚合应该简单、清晰和简洁。

尾巴

因此,样板文件已被处理,但请注意,您可能仍出于某种原因希望您的记录组件之一不被视为两个不同对象之间比较过程的一部分,这就是您可能想要覆盖equals的地方和hashCode提供的默认实现。此外,毫无疑问,我的想法是有时需要 a 的幻想,toString因此也需要覆盖它。

以上大多不能归类为编译或运行时失败,但提案本身读取了它带来的风险:

从状态描述自动派生的任何成员也可以显式声明。但是,粗心地实现访问器或 equals/hashCode 可能会破坏记录的语义不变量。

注意:后者主要是我的观点,这样消费者会希望有各种灵活性,以便他们可以使用最新的功能,但在某种程度上,现有的实现曾经可以工作。你看,向后兼容性在更大程度上很重要,因为在升级期间很好。)

于 2020-05-11T02:20:01.650 回答
1

什么是 Java 记录? 关于 Java 最常见的抱怨之一是您需要编写大量代码才能使一个类有用。很多时候,您需要编写以下内容:

  1. toString()
  2. 哈希码()
  3. 等于()
  4. 吸气剂方法
  5. 公共构造函数

对于简单的领域类,这些方法通常很无聊、重复,并且可以很容易地机械生成(IDE 通常提供这种能力),但到目前为止,语言本身并没有提供任何方法来做到这一点.

记录的目标是扩展 Java 语言语法并创建一种方式来表示类是“字段,只是字段,除了字段什么都没有”。通过您对一个类做出该声明,编译器可以通过自动创建所有方法并使所有字段参与方法(例如 hashCode())来提供帮助。

记录带有默认实现hashCode(),用于记录equals()toString()的所有属性

hashCode() 的默认实现

记录将使用记录内所有属性的哈希码

默认实现 equals()

记录将使用所有属性来决定两个记录是否相等

所以任何哈希实现,例如 HashSet、LinkedHashSet、HashMap、LinkedHashMap、

等将使用hashCode(),并在任何碰撞的情况下使用equals()

默认实现还是自定义实现?

如果你想使用所有属性hashCode()然后equals()不要覆盖

您是否需要为记录覆盖 hashCode() 和 equals()?

您可以保留默认实现或仅选择一些属性

任何东西,但是如果您想要自定义属性,您可以覆盖以决定哪些属性决定相等性和属性使 hashCode

默认实现中hashCode是如何计算的?

将使用整数和字符串的 hashCode,如下所示:

    int hashCode = 1 * 31;
    hashCode = (hashCode + "a".hashCode()) & 0x7fffffff;

, 下面的代码具有默认实现hashCode(),equals()

toString()

HashSet 没有添加它们两个,因为这两个记录具有相同的 hashCode 和 equals

    static record Record(int id, String name) {

    }

    public static void main(String[] args) {
        Record r1 = new Record(1, "a");
        Record r2 = new Record(1, "a");

        Set<Record> set = new HashSet<>();
        set.add(r1);
        set.add(r2);
        System.out.println(set);

        System.out.println("Hashcode for record1: " + r1.hashCode());
        System.out.println("Hashcode for record2: " + r2.hashCode());

        int hashCode = 1 * 31;
        hashCode = (hashCode + "a".hashCode()) & 0x7fffffff;
        System.out.println("The hashCode: " + hashCode);
    }

, 输出

[Record[id=1, name=a]]
Hashcode for record1: 128
Hashcode for record2: 128
The hashCode: 128

, 资源:

甲骨文

于 2020-05-11T04:49:08.200 回答