简体   繁体   English

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

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

Let's say i have this class:假设我有这门课:

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

    //Getters and setters
}

The field idFromDatabase is the attribute that should be verified in equals and used to create the hashCode.字段idFromDatabase是应在 equals 中验证并用于创建 hashCode 的属性。 But sometimes, i am working with a list of People in memory, and have not yet stored the objects on the database, so the idFromDatabase is null for all objects, which would cause hashCode to return the same value for every object.但有时,我正在处理内存中的 People 列表,并且尚未将对象存储在数据库中,因此所有对象的idFromDatabase为 null,这将导致 hashCode 为每个对象返回相同的值。 I solved this issue by adding the following to equals and hashCode metods:我通过在 equals 和 hashCode 方法中添加以下内容解决了这个问题:

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

and

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

It worked, but is it safe?它有效,但安全吗? Can i do it for every class that relies on a database field for equality check?我可以为每个依赖数据库字段进行相等性检查的类都这样做吗?

if(idFromDatabase == null) return super.equals(o); is incorrect as super's equals (if implemented correctly) does a getClass() check, which will of course be different, thus super.equals will always be false.不正确,因为 super 的 equals (如果正确实现)会执行getClass()检查,这当然会有所不同,因此super.equals将始终为 false。

From your description I'm inferring that when comparing two People objects:根据您的描述,我推断在比较两个People对象时:

  • If both have an ID, they are equal if they have the same ID, even if they have different names如果两者都有一个ID,即使他们有不同的名字,他们也有相同的ID
  • Otherwise, they are only equal if they are the same instance.否则,仅当它们是同一个实例时它们才相等。

If that's correct, then:如果这是正确的,那么:

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

As already noted by @Jeroen Vannevel, if you are likely to end up with having 2 or more objects not stored in database holding the exact same information, then this technique will not help you in identifying this.正如@Jeroen Vannevel 已经指出的那样,如果您最终可能有 2 个或更多对象未存储在包含完全相同信息的数据库中,那么此技术将无法帮助您识别这一点。

@Solver is also quite true in that a subclass is meant to have different behavior than its superclass, so you shouldn't return that they're equal. @Solver 也是正确的,因为子类的行为与其超类不同,因此您不应该返回它们相等。

However, in your particular example, you are just extending the Object class, so your assumption that it is safe is true (if we exclude the possibility of having 2 not-yet-persisted same Person s in memory).但是,在您的特定示例中,您只是扩展了Object类,因此您认为它是安全的假设是正确的(如果我们排除内存中有 2 个尚未持久化的相同Person的可能性)。

Object provides the most basic equals method : Object提供了最基本的equals 方法

For any non-null reference values x and y, this method returns true if and only if x and y refer to the same object ( x == y has the value true ).对于任何非空引用值 x 和 y,当且仅当 x 和 y 引用同一个对象( x == y的值为true )时,此方法才返回 true 。

The hashCode method of Object: Object 的hashCode 方法

As much as is reasonably practical, [...] does return distinct integers for distinct objects尽管合理可行,[...] 确实为不同的对象返回不同的整数

These definitions make it clear that if you're only extending Object , then this technique is safe.这些定义清楚地表明,如果您只是扩展Object ,那么这种技术是安全的。

There are a few problems with your reasoning.你的推理有一些问题。

  • equals and hashcode are not subtype-friendly so it doesn't make sense to start thinking about super calls, equalshashcode不是子类型友好的,所以开始考虑super调用是没有意义的,
  • super is Object anyway so it's equals and hashcode are useless in this context.无论如何superObject所以它是equalshashcode在这种情况下是无用的。
  • What if you have two Person objects referring to the same person, but only one is stored in the database.如果您有两个Person对象引用同一个人,但只有一个存储在数据库中,该怎么办? Are they the same or different?它们是相同的还是不同的?

One universal solution is to make two classes一种通用的解决方案是创建两个类

  1. Person which stores a 'local' person. Person存储“本地”的人。 Doesn't contain idFromDatabase ,不包含idFromDatabase
  2. StoredPerson which contains idFromDatabase and a Person (or all fields of Person , but this is harder to maintain) StoredPerson含有idFromDatabase和一个Person (或所有领域的Person ,但是这是难以维护)

This way, at least equals and hashcode are well-defined and well-behaved at all times.这样,至少equalshashcode码在任何时候都是明确定义和表现良好的。

Implementation and usage实现和使用

If you use any kind of Set / Map to store people, you now have two of them.如果您使用任何类型的Set / Map来存储人员,那么您现在拥有其中的两个。 When you save new Person s to database, you remove them from the 'local' Set / Map , wrap them in StoredPerson , and put them in the 'database' Set / Map .当您将新Person保存到数据库时,您将它们从“本地” Set / MapStoredPerson ,将它们包装在StoredPerson ,然后将它们放入“数据库” Set / Map

If you want a searchable list of all people, make one with all Person s from both datasets into one.如果您想要一个所有人的可搜索列表,请将两个数据集中的所有Person合并为一个。 When you find a Person you're interested in and want to retrieve the idFromDatabase , if any, then you'd do good to prepare a map from Person to StoredPerson beforehand.当您找到您感兴趣的Person并想要检索idFromDatabase (如果有),那么您最好事先准备好从PersonStoredPerson的映射。

Thus you need at least,因此你至少需要,

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

and something like this:和这样的事情:

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.

相关问题 在子类中调用super.equals和super.hashCode? - Calling super.equals and super.hashCode in child class? 覆盖等于时在派生类中调用super.equals() - calling super.equals() in derived class while overriding equals 覆盖equals和hashCode只是为了调用super.equals / hashCode或引发AssertionError? - Override equals and hashCode just to call super.equals/hashCode or throw AssertionError? 我应该如何使用Google guava hashCode()调用super.hashcode - how should I invoke super.hashcode with google guava hashCode() 为什么this.hashCode()和super.hashCode()在子类中返回相同的值? - why this.hashCode() and super.hashCode() returns the same value in subclass? 为什么 super.hashCode 对来自同一个 Class 的对象给出不同的结果? - Why does super.hashCode give different results on objects from the same Class? 从调用到super.equals的递归.equals方法 - Recursive .equals method from call to super.equals 继承 - 在子类中使用super.equals(),它覆盖在超类的equals中使用的方法 - Inheritance - using super.equals() in subclasses that override methods used in equals of superclass 用超类hashCode和Object覆盖hashCode - Override hashCode with super class hashCode and Objects 如何找出我是否在super.equals调用链中到达了Object? - How to find out if I have reached Object in a chain of super.equals calls?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM