3

我在写出标题后阅读了这篇 SO帖子,但仍然决定解决关于 Java 中 equals 的防错实现的问题。这是我的正常实现

@Override
        public boolean equals(Object o){
            if(o == null) return false;
            if(o instanceof CompositePk == false) return false;
            if(this == o) return true;
            CompositePk that = (CompositePk)o;
            return new EqualsBuilder().append(this.id, that.id)
                                      .append(this.bucketId, that.bucketId)
                                      .isEquals();
        }

使用 Apache 的 EqualsBuilder 来完成日常工作。比这更容易的是我的 Netbean 自动生成的equals(o)实现

 @Override
        public boolean equals(Object obj){
        if (obj == null) {
                return false;
            }
            if (getClass() != obj.getClass()) {
                return false;
            }
            final TemplatesWrapper other = (TemplatesWrapper) obj;
            if (this.timeAdded != other.timeAdded && (this.timeAdded == null || !this.timeAdded.equals(other.timeAdded))) {
                return false;
            }
            return true;
    }

我从 2 个 diff 项目中获取这些,但他们都试图完成相同的事情,但使用的是 diff 方法。你更喜欢哪种风格,或者你发现了什么缺陷?

4

5 回答 5

9

首先,不需要测试null,然后测试 instanceof,因为foo instanceof Bar计算结果为falsewhen foois null

instanceof将运算符的结果与 进行比较很奇怪false,因为这instanceof是一个布尔运算。

将课程与 进行比较getClass()充其量是有争议的。Joshua Bloch,他编写了大部分 Java 集合框架以及许多其他重要的东西,他说

这种技术(“基于 getClass 的 equals 方法”)确实满足 equals 契约,但代价高昂。getClass 方法的缺点是它违反了“Liskov 替换原则”,该原则指出(粗略地说)期望超类实例的方法在呈现子类实例时必须正确运行。如果子类添加了一些新方法,或者对行为进行了简单的修改(例如,通过在每个方法调用时发出跟踪),当子类和超类实例不能正确交互时,程序员会感到惊讶。“应该相等”的对象不会,导致程序失败或行为不正常。Java的集合基于equals方法这一事实加剧了这个问题。

您应该使用instanceof而不是比较 via getClass(),除非您有一些特定的技术原因不这样做。

在确定另一个对象可与 进行this比较后,然后将原语与 进行比较,将==对象与进行比较equals。如果您的任何成员对象都可以为空,那就更复杂了;然后,您必须编写详细的子句以将可能为空的事物相互比较(或编写bothNullOrEqual(Object a, Object b)方法)。

这种EqualsBuilder方法在我看来是假的,但这只是一种“气味”,我不会在技术上反对。一般来说,我不喜欢在可能被频繁调用的方法中进行额外的方法调用。

Apache 是假的,因为它测试null并使用了getClass()比较。

这是我的:

@Override
public boolean equals(final Object o) {
    if (!(o instanceof MyClass))
        return false;
    final MyClass om = (MyClass)o;
    // compare om's fields to mine
}
于 2009-10-15T00:45:44.117 回答
5

我会这样做:

public boolean equals(Object ob) {
  if (ob == null) return false;
  if (ob == this) return true;

  if (!(ob instanceof MyClass)) return false; // OR
  if (ob.getClass() != getClass()) return false;

  // check relevant members
}

中间的两条线是不同的。一个允许子类相等(第一个),另一个不允许。使用任何一个合适的。

举个例子,Java 的AbstractList类可能会使用第二种形式,因为它的确切实现List是无关紧要的。重要的是成员是否平等并处于相同的位置。

相反, Person 类应该使用第一种形式(instanceof),因为如果有 Student 子类并且您调用Person.equals(Student)它可能会返回 true 而无需检查 Student 中的额外字段,而 Student.equals(Person) 可能会返回false。如果equals()不是可交换的,那你就是在自找麻烦。

我倾向于使用equals()由我的 IDE (IntelliJ IDEA) 生成的方法,而不是创建对某些 Apache 库的不必要的依赖,以获取很少的收益。

于 2009-10-15T00:28:39.790 回答
0

老实说,你必须编写的代码越少,你就越好(在大多数情况下)。

生成的代码已被许多人调试和使用。您不妨使用生成的内容(如果您需要提高性能,请这样做)。

使用生成代码的优势:只要您的实例字段发生更改(并且此生成的代码没有被修改),您就可以简单地重新生成代码。

有时,更容易考虑可维护性。经验法则:您自己编写的代码越少,您需要调试的代码就越少。如果生成的代码不会对性能造成巨大影响,请生成它!

于 2009-10-15T16:31:14.440 回答
0

Apache 的比你的或 cletus 的要好。

就我模糊的记忆而言,使用instanceofin equals 存在问题;我还不能完全解释为什么,也许有人会详细说明。我可能是错的。

- 编辑:

正如ChrisSteve在下面帮助解释的那样,我正在考虑 equals 实现的“对称属性”。在此基础上,我可以支持我更喜欢 Apache 实现的主张:)

于 2009-10-15T00:32:21.390 回答
0

说明:当覆盖 equals 方法时,hashCode() 方法也必须被覆盖。因此,考虑如下所示的具有 3 个属性的类,并考虑到所有属性对相等性都很重要,equals() 实现必须测试所有这些字段。条件的顺序并不重要,但必须对所有字段进行相等性测试才能完全考虑对象之间的相等性。

public class SampleClass {

  private Long id;
  private String description;
  private Date creation;

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((creation == null) ? 0 : creation.hashCode());
        result = prime * result + ((description == null) ? 0 : description.hashCode());
        result = prime * result + ((id == null) ? 0 : id.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        boolean isEquals = true;
        if (this == obj) { isEquals = true; }
        else if (obj == null) { isEquals = false; } 
        else if (getClass() != obj.getClass()) { isEquals = false;  }
        else { 
            SampleClass other = (SampleClass) obj;
            if (creation == null) {
                if (other.creation != null) isEquals = false;
            } else if (!creation.equals(other.creation)) {
                isEquals = false;
            } else if (description == null) {
                if (other.description != null) isEquals = false;
            } else if (!description.equals(other.description)) {
                isEquals = false;
            } else  if (id == null) {
                if (other.id != null) isEquals = false;
            } else if (!id.equals(other.id)) {
                isEquals = false;
            }
        }
        return isEquals;
    }
于 2014-09-25T11:40:31.517 回答