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

时间:2015-09-04 18:25:05

标签: java

我们说我有这个课程:

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

    //Getters and setters
}

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

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

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

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

4 个答案:

答案 0 :(得分:2)

if(idFromDatabase == null) return super.equals(o);不正确,因为超级等于(如果正确实施)会进行getClass()检查,这当然会有所不同,因此super.equals将始终为假。

答案 1 :(得分:1)

根据您的描述,我推断在比较两个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());
}

答案 2 :(得分:1)

正如@Jeroen Vannevel已经指出的那样,如果你最终可能有2个或更多的对象没有存储在数据库中并保存完全相同的信息,那么这种技术无法帮助你识别它。

@Solver也是如此,因为子类的行为与其超类的行为不同,所以你不应该返回它们是相等的。

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

Person提供最基本的equals method

  

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

对象的hashCode method

  

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

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

答案 3 :(得分:1)

你的推理存在一些问题。

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

一个通用的解决方案是制作两个类

  1. Person存储了本地'人。不包含idFromDatabase
  2. StoredPerson包含idFromDatabasePerson(或Person的所有字段,但这很难维护)
  3. 这样,至少equalshashcode定义明确,并且始终表现良好。

    实施和使用

    如果您使用任何类型的Set / Map来存储人,那么您现在有两个人。将新的Person保存到数据库后,您可以将其从“本地”'中删除。 Set / Map将其封装在StoredPerson中,并将其放入数据库' Set / Map

    如果您想要一个包含所有人的可搜索列表,请将两个数据集中的所有Person制成一个。如果您发现自己感兴趣的Person并想要检索idFromDatabase(如果有的话),那么您可以自行准备从Person到{StoredPerson的地图{1}}事先。

    因此至少需要

    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);
        }
    }