繁体   English   中英

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

[英]Is it a bad practice to return super.equals and super.hashcode in a class?

假设我有这门课:

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

    //Getters and setters
}

字段idFromDatabase是应在 equals 中验证并用于创建 hashCode 的属性。 但有时,我正在处理内存中的 People 列表,并且尚未将对象存储在数据库中,因此所有对象的idFromDatabase为 null,这将导致 hashCode 为每个对象返回相同的值。 我通过在 equals 和 hashCode 方法中添加以下内容解决了这个问题:

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

if(idFromDatabase == null) return super.hashCode();

它有效,但安全吗? 我可以为每个依赖数据库字段进行相等性检查的类都这样做吗?

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

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

  • 如果两者都有一个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 已经指出的那样,如果您最终可能有 2 个或更多对象未存储在包含完全相同信息的数据库中,那么此技术将无法帮助您识别这一点。

@Solver 也是正确的,因为子类的行为与其超类不同,因此您不应该返回它们相等。

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

Object提供了最基本的equals 方法

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

Object 的hashCode 方法

尽管合理可行,[...] 确实为不同的对象返回不同的整数

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

你的推理有一些问题。

  • equalshashcode不是子类型友好的,所以开始考虑super调用是没有意义的,
  • 无论如何superObject所以它是equalshashcode在这种情况下是无用的。
  • 如果您有两个Person对象引用同一个人,但只有一个存储在数据库中,该怎么办? 它们是相同的还是不同的?

一种通用的解决方案是创建两个类

  1. Person存储“本地”的人。 不包含idFromDatabase
  2. StoredPerson含有idFromDatabase和一个Person (或所有领域的Person ,但是这是难以维护)

这样,至少equalshashcode码在任何时候都是明确定义和表现良好的。

实现和使用

如果您使用任何类型的Set / Map来存储人员,那么您现在拥有其中的两个。 当您将新Person保存到数据库时,您将它们从“本地” Set / MapStoredPerson ,将它们包装在StoredPerson ,然后将它们放入“数据库” Set / Map

如果您想要一个所有人的可搜索列表,请将两个数据集中的所有Person合并为一个。 当您找到您感兴趣的Person并想要检索idFromDatabase (如果有),那么您最好事先准备好从PersonStoredPerson的映射。

因此你至少需要,

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

和这样的事情:

void savePerson(Person person) {
    synchronized (lockToPreserveInvariants) {
        int id = db.insert(person);

        StoredPerson sp = new StoredPerson(id, person);

        localPeople.remove(person);
        storedPeople.put(person, sp);
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM