Предупреждение Findbugs: метод Equals не должен предполагать ничего о типе своего аргумента

При запуске 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
}
12.12.2008 23:04:20
это звучит немного странно. Пожалуйста, покажите нам код.
Ray Tayek 12.12.2008 23:11:12
4 ОТВЕТА
РЕШЕНИЕ

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

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

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

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

38
23.05.2017 10:29:36
По сути, это означает, что вместо вызова ClassCastException ваш метод equals () должен возвращать false.
Darron 12.12.2008 23:13:35
Но правильная ли это практика в таких ситуациях? Я имею в виду, в идеале мне бы понравилась проверка типов во время компиляции, которую я не могу получить из-за ограничений Java.
Uri 12.12.2008 23:15:14
Почему? Что плохого в проверке на равенство между несвязанными типами? Это вопрос с совершенно прекрасным и четким ответом.
Joachim Sauer 12.12.2008 23:22:49
Черт, слишком поздно, чтобы забрать мое понижение! Хотя я до сих пор не согласен (пока), я делать согласен , что InstanceOf против того же класса не режется и сушат. Посмотрим на это дальше - спасибо за ссылки.
Lawrence Dol 13.12.2008 22:47:45
Хотя этот код решает исходную проблему FindBugs, похоже, что в результате FindBugs теперь помечает код с помощью NP_EQUALS_SHOULD_HANDLE_NULL_ARGUMENT. Так что вам, вероятно, нужно добавить пустую проверку, например:if ( obj2 == null || getClass() != obj2.getClass() )
Cincinnati Joe 29.04.2011 14:29:45

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

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).

7
12.12.2008 23:22:09

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

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

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

0
12.12.2008 23:17:14
Нулевая проверка избыточна с instanceof. Будьте осторожны, используя instanceof; если в уточненном контракте не финальных равных не прописаны условия равенства (как это делает java.util.Set), вы, вероятно, нарушите требование симметрии.
erickson 18.12.2008 06:56:48

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

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

2
10.06.2010 15:09:51