【问题标题】:Is it proper for equals() to depend only on an ID?equals() 仅依赖于 ID 是否合适?
【发布时间】:2013-06-04 21:38:06
【问题描述】:

假设我有课 User:

public class User {
  private Long id;
  private String name;
  private Integer age;
  private BigDecimal account;
  // other fields, getters and setters
}

如下覆盖equals方法是否合适?

@Override
public boolean equals(Object ob) {
   if (ob == null) {
       return false;
   }
   if (this == ob) {
       return true;
   }
   if (ob instanceof User) {
       User other = (User) ob;
       return this.id.equals(other.getId());
   }
   return false;
}

事实证明,一个对象的唯一性仅由其 ID 决定。但在我的应用程序中,id 始终是唯一的。它在数据库中提供。我的equals 实现是否足以解决这个问题?或者这不是最佳做法?

当然我知道在这种情况下hashCode 的实现应该如下:

@Override
public int hashCode() {
   return id.intValue();
}

【问题讨论】:

  • 如果有的话,您需要.equals() 做什么?
  • 看起来你很好。如果对象在 id 相等时也相等,则说明代码正确。
  • User 被持久化之前,id 可以为空,然后equals 将抛出。但这只是一个疣。我经常看到你的模式。一个 SO 问题导致了这一点 Hibernate 文档,这对您可能无关紧要:docs.jboss.org/hibernate/core/4.0/manual/en-US/html/…
  • id -> (name , age ,account) .. 你依赖于数据库约束.. 你应该将 id 字段设为最终(如果你没有使用休眠)
  • 因为idLong 你的hashCode 方法可以是return id.hashCode()

标签: java oop overriding equals hashcode


【解决方案1】:

您是否应该这样做取决于您的类的语义。也就是说,说你的类的两个对象是等价的是什么意思

最重要的区别在于具有值语义的对象和具有实体语义的对象。实体对象不是等价的,即使它们具有等价的属性(颜色、长度等)。在许多情况下,包括从具有主键的数据库表中读取对象时,实体对象将具有唯一的 ID 字段。在这种情况下只比较 ID 字段是正确的做法。

【讨论】:

  • 如果User 对象代表数据库中某个用户的特征,对于任何给定ID,是否可以同时存在多个这样的对象?似乎为了保持一致性,最好让所有此类对象都由工厂方法构造,该方法使用某种弱引用集合以确保只要存在对具有某些 ID 的 User 的任何引用,任何请求获取具有该 ID 的 User 将返回相同的对象。如果这样做了,User 就不需要覆盖equals
【解决方案2】:

我同意它在身份证上。 但是我在获取应该更新数据库的数据时遇到了麻烦。 在这个带有用户的示例中,equals 只查看了我创建的 ID。

interface DataEquals<T extends DataEquals> {
   public boolean isDataEquals(T other)
}


User implements DataEquals<User> {
   public boolean isDataEquals(User other) {
      boolean b1 = getName().equals(other.getName());
      boolean b2 = getAge().equals(other.getAge());
      boolean b3 = getAccount().equals(other.getAccount());
      return b1 && b2 && b3;
   }
}

有了这个,我们就可以拥有这个了。

public class ListChanges<T extends DataEquals<T>> {

  private List<T> added = new ArrayList<T>();
  private List<T> removed = new ArrayList<T>();
  private List<T> changed = new ArrayList<T>();
  private List<T> unchanged = new ArrayList<T>();

  public ListChanges() {
    super();
  }
  public List<T> getAdded() {
    return added;
  }
  public List<T> getChanged() {
    return changed;
  }
  public List<T> getRemoved() {
    return removed;
  }
  public List<T> getUnchanged() {
    return unchanged;
  }

  public boolean hasAnyChanges() {
    return added.size()>0 || removed.size()>0 || changed.size()>0;
  }

  public void parse(List<T> oldList,List<T> newList) {
    for (T oldObj : oldList) {
        int index =newList.indexOf(oldObj);
        if (index==-1) {
            removed.add(oldObj);
        } else {
            T newObj = newList.get(index);

            if (newObj.isDataEquals(oldObj)) {
                unchanged.add(oldObj);
            } else {
                changed.add(newObj);
            }
        }
    }
    for (T newObj : newList) {
        if (oldList.indexOf(newObj)==-1) {
            added.add(newObj);
        }
    }
 }
}

那么我们就可以这样做了

List<User> oldList = ....;
List<User> newList = ...;
ListChanges<User> listChanges = new ListChanges<User>();
listChanges.parseChanges(oldList,newList);

您是否同意这是一种方法。 ??????

【讨论】:

    【解决方案3】:

    只要您不必将尚未持久化的实体与数据库进行比较,就可以将 id 用于equals 方法。如果要比较尚未保存的实体,则必须比较它们的属性。

    【讨论】:

      【解决方案4】:

      您的 equals() 方法看起来不像是由 IDE 生成的,因为它不检查 @Eric 所述的“id”空值。

      这就是我的 equals()/hashCode() 方法使用相同的 id 属性的样子

      @Override
      public int hashCode() {
          final int prime = 31;
          int result = 1;
          result = prime * result + ((id == null) ? 0 : id.hashCode());
          return result;
      }
      
      @Override
      public boolean equals(Object obj) {
          if (this == obj)
              return true;
          if (obj == null)
              return false;
          if (getClass() != obj.getClass())
              return false;
          User11 other = (User11) obj;
          if (id == null) {
              if (other.id != null)
                  return false;
          } else if (!id.equals(other.id))
              return false;
          return true;
      }
      

      我们应该尽可能使用自动生成样板代码,因为这样更不容易出错。

      另外,关于您关于“id”道具唯一性的观点,这取决于您的偏好以及您希望如何使用您的 equals 方法(业务要求),即如果两个用户的名称相同,则在比较两个时是否将它们视为相同用户对象稍后..

      【讨论】:

        【解决方案5】:

        没关系。只要没有两个不同的用户可以拥有相同的 ID,您的 equals 函数就足够了。如果一个用户可以用不同的 ID 表示两次(无论出于何种原因)并且您确实希望将他们视为平等,则可能会出现问题。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-07-22
          • 1970-01-01
          • 2015-09-22
          • 2015-01-09
          • 2017-02-21
          • 1970-01-01
          相关资源
          最近更新 更多