Findbugs警告:Equals方法不应该假设其参数的类型

时间:2008-12-12 23:04:20

标签: java equals findbugs

在我的项目上运行FindBugs时,我得到了一些上述错误的实例。

即,我的重写版本的equals将RHS对象强制转换为与定义覆盖版本的对象相同的类型。

但是,我不确定是否可以使用更好的设计,因为AFAIK Java不允许方法参数的变化,因此无法为equals参数定义任何其他类型。

我做错了什么,或者FindBugs是否过于渴望?

用这句话来表达这个问题的另一种方法是:如果传递给equals的对象与LHS的类型不同,那么正确的行为是什么:这是假的,还是应该有异常?

例如:

public boolean equals(Object rhs)
{
    MyType rhsMyType = (MyType)rhs; // Should throw exception
    if(this.field1().equals(rhsMyType.field1())... // Or whatever
}

4 个答案:

答案 0 :(得分:33)

通常,在实现equals时,您可以在强制转换之前检查参数的类是否与实现类相等(或兼容)。像这样:

if (getClass() != obj.getClass())
    return false;
MyObj myObj = (MyObj) obj;

这样做会阻止FindBugs警告。

发表评论的附注:
有些人主张使用instanceof代替getClass来检查类型安全性。关于这一点存在很大争议,当我注意到你可以检查类相等兼容性时,我试图不去讨论,但我想我无法逃脱它。归结为此 - 如果使用instanceof,您可以支持类的实例与其子类的实例之间的相等性,但您可能会破坏equals的对称契约。一般情况下,我建议您不要使用instanceof,除非您知道自己需要它,并且知道自己在做什么。有关更多信息,请参阅:

答案 1 :(得分:7)

你可能正在做这样的事情:

public class Foo {
  // some code

  public void equals(Object o) {
    Foo other = (Foo) o;
    // the real equals code
  }
}

在这个例子中,你假设有关于equals()的参数:你假设它是Foo类型。不一定是这种情况!你也可以得到一个String(在这种情况下你几乎肯定会返回false)。

所以你的代码应该是这样的:

public void equals(Object o) {
  if (!(o instanceof Foo)) {
    return false;
  }
  Foo other = (Foo) o;
  // the real equals code
}

(或使用Dave L提到的更严格的getClass() != o.getClass()

你也可以这样看待它:

Integer i = new Integer(42);
String s = "fourtytwo";
boolean b = i.equals(s);

是否有任何理由认为此代码应该抛出ClassCastException而不是正常完成并将b设置为false

投掷ClassCastException作为对.equals()的回复是不明智的。因为即使这是一个愚蠢的问题(“当然一个字符串永远不会等于一个Foo!”)它仍然是一个有效的答案(“no”== false)。

答案 2 :(得分:2)

我建议忽略所说的findbugs警告。实际上,如果使用意外类的对象调用equals,那几乎肯定是一个错误,并且你想在bug上快速失败。

例如,如果您有一个'ArrayList files'并调用files.contains(“MyFile.txt”),那么如果您遇到ClassCastException就会很好。相反,Java只返回false,并且可能需要很长时间才能发现该错误。

答案 3 :(得分:0)

我开始像这样的equals(Object)实现:

if ((object == null) || !(object instaceof ThisClass)) {
    return false;
}

这也会阻止FindBugs警告,但是当提交ThisClass的子类时,它不会自动返回false。它也可能被认为是相同的,特别是如果它的equals(Object)方法还没有重写的。