等方法中身份测试的必要性

时间:2013-05-30 18:51:26

标签: java equals apache-commons compareto

编辑:事情的核心

当传统的equals方法的其余部分失败时,Identity Test何时通过?这是为了节省额外工作的时间吗?


原帖

我在我正在测试的课程中使用CompareToBuilder中的org.apache.commons.lang3.builder.CompareToBuilder。我注意到EqualsBuilder要求在调用equals构建器之前显式调用以下代码。

if (obj == this) { return true; } // Identity test

这种逻辑也出现在Eclipse自动生成的equals方法中。我正在尝试使用DRY方法,让我的equals方法只需调用我的compareTo方法并将等效性测试为0。

一个问题是我是否需要在我的equals方法中包含上述代码,将其添加到我的compareTo方法中,或者是否已由CompareToBuilder覆盖。我注意到CompareToBuilder检查传递的参数的等价性,但是没有接收到对原始lhs(this)和rhs(Object obj)的任何直接引用。这让我认为这是一个疏忽,我应该在compareTo方法中纠正。

我最大的问题是我似乎无法设计一个潜在的测试用例obj == this但是this.compareTo(obj) != 0只有想到的事情是在一个未正确实现的比较中如果没有首先检查this中的相应变量是否也为空,则在obj中发送其实例变量之一null可能会返回非零数字。 (昨天进入这个。)

示例等于方法:

@Override
public boolean equals(Object obj) {
    if (obj != null && obj instanceof MyClass)
        return this.compareTo((MyClass)obj) == 0;
    return false;
}

示例compareTo方法

@Override
public int compareTo(MyClass other) {
return new CompareToBuilder()
    .append(this.getParam1(), other.getParam1())
    .append(this.getParam2(), other.getParam2())
    .toComparison();
}

3 个答案:

答案 0 :(得分:3)

查看Comparable的文档,compareTo()需要

x.compareTo(y) == -y.compareTo(x)

因此,如果是x == yx.compareTo(x) == -x.compareTo(x),那么x.compareTo(x) == 0。因此,x == y && x.compareTo(y) != 0的唯一方法是打破compareTo()合同。

请注意,在我的理解中,obj == this测试往往被添加到equals()方法作为优化,以防止在不必要时评估深度比较并且不影响调用的结果。 (参见Effective Java 2nd Ed,Item 8.)我假设这里的compareTo()方法也是如此。

答案 1 :(得分:1)

  

当传统的equals方法的其余部分失败时,Identity Test何时通过?这是为了节省额外工作的时间吗?

简短的回答是:永远不会。

身份测试是一种廉价的优化,旨在快速解决equals()方法的其他问题。实际上,Joshua Bloch建议每个equals()方法都以优化测试开始(Effective Java,2nd Ed。)。

先生。 Bloch建议使用equals()的以下一般结构:

  1. 使用==运算符检查参数是否是对此对象的引用。如果是,则返回true。

  2. 使用instanceof运算符检查参数的类型是否正确。如果不是,则返回false。

  3. 将参数转换为正确的类型。

  4. 对于类中的每个重要字段,检查参数的该字段是否与此对象的相应字段匹配。

答案 2 :(得分:1)

  

当传统的等于失败时,身份测试何时通过?

从技术上讲,如果“传统”也意味着“天真”,那么带有循环的对象图(例如A有一个指向B的ivar,其中有一个指向A的ivar)会通过身份测试,但是传统的等于实现将进入无限循环。

我不确定Apache库是否可以防止这种情况。