Предупреждение Findbugs: метод Equals не должен предполагать ничего о типе своего аргумента - PullRequest
16 голосов
/ 13 декабря 2008

При запуске 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 ]

33 голосов
/ 13 декабря 2008

Как правило, при реализации equals вы можете проверить, равен ли класс аргумента (или совместим) классу реализации, перед его приведением. Примерно так:

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

Делая это таким образом, вы получите предупреждение FindBugs.

Примечание для комментария:
Некоторые люди утверждают, что для проверки безопасности типов используйте instanceof вместо getClass. Существует большая дискуссия по этому вопросу, в которую я пытался не ввязываться, когда отмечал, что вы можете проверить на совместимость классов или , но, думаю, мне не избежать этого. Это сводится к следующему - если вы используете instanceof, вы можете поддерживать равенство между экземплярами класса и экземплярами его подкласса, но вы рискуете нарушить симметричный контракт equals. Как правило, я бы рекомендовал не использовать instanceof, если вы не знаете, что вам это нужно, и не знаете, что делаете. Для получения дополнительной информации см .:

7 голосов
/ 13 декабря 2008

Вы, вероятно, делаете что-то вроде этого:

public class Foo {
  // some code

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

В этом примере вы предполагаете что-то об аргументе equals (): вы предполагаете, что он имеет тип Foo. Это не должно быть дело! Вы также можете получить строку (в этом случае вы почти наверняка должны вернуть false).

Итак, ваш код должен выглядеть так:

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

(или используйте более строгий getClass() != o.getClass(), упомянутый Дэйвом Л.

Вы также можете посмотреть на это так:

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

Есть ли причина, по которой этот код должен выдавать ClassCastException вместо обычного завершения и устанавливать b в false?

Бросок ClassCastException в ответ на .equals() не будет разумным. Потому что, даже если это глупый вопрос («Конечно, строка никогда не равна Foo!»), Он все равно остается верным с совершенно точным ответом («нет» == false).

2 голосов
/ 10 июня 2010

Я бы рекомендовал игнорировать упомянутое предупреждение о найденных ошибках. На практике, если equals вызывается с объектом неожиданного класса, это почти наверняка ошибка, и вы хотите быстро потерпеть неудачу при ошибках.

Например, если у вас есть «ArrayList files» и вызываете files.contains («MyFile.txt»), было бы хорошо, если бы вы получили ClassCastException. Вместо этого Java просто возвращает false, и, вероятно, потребуется много времени, пока вы не обнаружите эту ошибку.

0 голосов
/ 13 декабря 2008

Я запускаю свои реализации equals (Object) следующим образом:

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

Это также предотвратит предупреждение FindBugs, но не будет автоматически возвращать false при передаче подкласса ThisClass. Его также можно считать равным, особенно если его метод equals(Object) не был переопределен.

...