一个有效的equals(Object o)实现

时间:2009-10-15 00:24:25

标签: java performance equals

在我写完标题之后,我读了这个SO post,但仍然决定在Java中讨论有关bug的bug实现问题。这是我的正常实现

@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方法。您认为哪种风格或是否存在任何缺陷?

5 个答案:

答案 0 :(得分:9)

首先,没有必要测试null,然后测试instanceof,因为foo instanceof Barfalsefoo时评估为null

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

将课程与getClass()进行比较至多是有争议的。 Joshua Bloch撰写了大量Java集合框架以及许多其他重要内容,says

  

这种技术(“getClass-based equals   方法“)确实满足等于   合同,但成本很高。该   getClass方法的缺点   是它违反了“利斯科夫   替代原则,“陈述   (粗略地说)一种方法   期待一个超类实例必须   当提出一个时,表现得很好   子类实例。如果子类添加   一些新的方法,或平凡的   修改行为​​(例如,通过发出一个   跟踪每个方法调用),   程序员会很惊讶   子类和超类实例   不要正常交互。对象那个   “应该是平等的”不会,导致   程序失败或表现   不正常。问题是   Java的事实加剧了这一点   集合基于平等   方法

您应该使用instanceof而不是通过getClass()进行比较,除非您有某些特定的技术原因。

在确定其他对象与this相当之后,您可以将基元与==和对象与equals进行比较。如果您的任何成员对象可以为null,则会更复杂;然后你必须编写详细的子句来比较可能为空的东西(或写一个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
}

答案 1 :(得分: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()不是可交换的,那你就是在寻找麻烦。

我倾向于使用我的IDE(IntelliJ IDEA)生成的equals()方法,而不是为某些Apache库创建不必要的依赖,但收效甚微。

答案 2 :(得分:0)

Apache比你的更好或者是cletus'。

就我的模糊记忆所暗示,在等号中使用instanceof存在问题;我还不能完全理解为什么,也许有人会详细说明。我错了。

- 编辑:

由于ChrisSteve有助于下面的解释,我正在考虑平等实施的“symmetric property”。在此基础上,我可以支持我更喜欢Apache实现的主张:)

答案 3 :(得分:0)

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

生成的代码已被许多人调试和使用。您也可以使用生成的内容(如果您需要提高性能,请执行此操作)。

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

有时候,考虑可维护性会更容易。经验法则:您自己编写的代码越少,您调试的代码就越少。如果生成的代码没有产生巨大的性能损失,请生成它!

答案 4 :(得分: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;
    }