Java 在类中返回super.equals和super.hashcode是一种不好的做法吗?

Java 在类中返回super.equals和super.hashcode是一种不好的做法吗?,java,Java,假设我有一门课: public class Person { private Integer idFromDatabase; private String name; //Getters and setters } 字段idFromDatabase是应在equals中验证并用于创建哈希代码的属性。但有时,我处理内存中的人员列表,但尚未将对象存储在数据库中,因此idFromDatabase对于所有对象都为空,这将导致hashCode为每个对象返回相同的值。 我通过在eq

假设我有一门课:

public class Person {
    private Integer idFromDatabase;
    private String name;

    //Getters and setters
}
字段
idFromDatabase
是应在equals中验证并用于创建哈希代码的属性。但有时,我处理内存中的人员列表,但尚未将对象存储在数据库中,因此
idFromDatabase
对于所有对象都为空,这将导致hashCode为每个对象返回相同的值。 我通过在equals和hashCode方法中添加以下内容来解决此问题:

if(idFromDatabase == null) return super.equals(o);


它起作用了,但安全吗?我可以为依赖数据库字段进行相等性检查的每个类执行此操作吗?

如果(idFromDatabase==null)返回super.equals(o)
是不正确的,因为super的equals(如果实现正确)执行了
getClass()
检查,这当然会有所不同,因此
super.equals
将始终为false。

如果(idFromDatabase==null)返回super.equals(o)
是不正确的,因为super的equals(如果实现正确)执行了
getClass()
检查,这当然是不同的,因此
super.equals
将始终是错误的。

根据您的描述,我推断,在比较两个
对象时:

  • 如果两者都有一个ID,那么如果它们有相同的ID,即使它们有不同的名称,它们也是相等的
  • 否则,只有当它们是同一实例时,它们才相等
如果这是正确的,那么:

@Override
public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (this.idFromDatabase == null)
        return false;
    if (! (obj instanceof People))
        return false;
    People that = (People)obj;
    if (that.idFromDatabase == null)
        return false;
    return this.idFromDatabase.equals(that.idFromDatabase);
}
@Override
public int hashCode() {
    // Use super.hashCode to distribute objects without an idFromDatabase
    return (this.idFromDatabase != null ? this.idFromDatabase.hashCode() : super.hashCode());
}

根据您的描述,我推断,当比较两个
对象时:

  • 如果两者都有一个ID,那么如果它们有相同的ID,即使它们有不同的名称,它们也是相等的
  • 否则,只有当它们是同一实例时,它们才相等
如果这是正确的,那么:

@Override
public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (this.idFromDatabase == null)
        return false;
    if (! (obj instanceof People))
        return false;
    People that = (People)obj;
    if (that.idFromDatabase == null)
        return false;
    return this.idFromDatabase.equals(that.idFromDatabase);
}
@Override
public int hashCode() {
    // Use super.hashCode to distribute objects without an idFromDatabase
    return (this.idFromDatabase != null ? this.idFromDatabase.hashCode() : super.hashCode());
}

正如@Jeroen Vannevel所指出的,如果您最终可能会有两个或多个对象没有存储在数据库中,而这些对象包含完全相同的信息,那么这种技术将无法帮助您识别这些信息

@解算器也非常正确,因为子类的行为与其父类不同,所以您不应该返回它们相等的结果

但是,在您的特定示例中,您只是扩展了
对象
类,因此您认为它是安全的假设是正确的(如果我们排除内存中有两个尚未持久化的
的可能性)

对象
提供了最基本的:

对于任何非空引用值x和y,此方法返回true 当且仅当x和y引用同一对象 (
x==y
的值为
true

对象的名称:

尽可能实际,[…]会为不同的对象返回不同的整数


这些定义清楚地表明,如果您只扩展
对象
,那么这种技术是安全的。

正如@Jeroen Vannevel所指出的,如果您可能最终有两个或多个对象没有存储在数据库中,而数据库中包含完全相同的信息,那么这种技术将无法帮助您识别这一点

@解算器也非常正确,因为子类的行为与其父类不同,所以您不应该返回它们相等的结果

但是,在您的特定示例中,您只是扩展了
对象
类,因此您认为它是安全的假设是正确的(如果我们排除内存中有两个尚未持久化的
的可能性)

对象
提供了最基本的:

对于任何非空引用值x和y,此方法返回true 当且仅当x和y引用同一对象 (
x==y
的值为
true

对象的名称:

尽可能实际,[…]会为不同的对象返回不同的整数


这些定义清楚地表明,如果您只扩展
对象
,那么这种技术是安全的。

您的推理存在一些问题

  • equals
    hashcode
    不是子类型友好的,因此开始考虑
    super
    调用是没有意义的
  • super
    无论如何都是
    对象
    ,因此它的
    等于
    hashcode
    在这个上下文中是无用的
  • 如果有两个
    Person
    对象引用同一个人,但数据库中只存储一个,该怎么办。它们是相同的还是不同的
一个通用的解决方案是创建两个类

  • Person
    用于存储“本地”人员。不包含
    idFromDatabase
  • StoredPerson
    ,其中包含
    idFromDatabase
    Person
    (或
    Person
    的所有字段,但这更难维护)
  • 这样,至少
    equals
    hashcode
    在任何时候都是定义良好、行为良好的

    实施和使用 如果您使用任何类型的
    集合
    /
    地图
    来存储人员,您现在有两个。当您将新的
    人员
    保存到数据库时,您可以将其从“本地”
    集合
    /
    映射
    中删除,将其包装在
    存储人员
    中,并将其放入“数据库”
    集合
    /
    映射

    如果您想要所有人的可搜索列表,请将两个数据集中的所有
    Person
    s合并为一个。当您找到感兴趣的
    并想从数据库中检索
    idFromDatabase
    时,如果有,您最好事先准备一张从
    StoredPerson
    的地图

    所以你至少需要

    Set<Person> localPeople = new HashSet<>();
    Map<Person, StoredPerson> storedPeople = new HashMap<>();
    

    你的推理有几个问题

    • equals
      hashcode
      不是子类型友好的,因此开始考虑
      super
      调用是没有意义的
    • super
      对象
      所以它是
      相等的
      
      void savePerson(Person person) {
          synchronized (lockToPreserveInvariants) {
              int id = db.insert(person);
      
              StoredPerson sp = new StoredPerson(id, person);
      
              localPeople.remove(person);
              storedPeople.put(person, sp);
          }
      }