Предупреждение об ошибках:Метод Equals не должен ничего предполагать о типе своего аргумента.
Вопрос
При запуске FindBugs в моем проекте я несколько раз получал описанную выше ошибку.
А именно, мои переопределяющие версии равенства приводят объект RHS к тому же типу, что и объект, в котором определена переопределяющая версия.
Однако я не уверен, возможен ли лучший дизайн, поскольку AFAIK Java не допускает различий в параметрах метода, поэтому невозможно определить какой-либо другой тип для параметра равенства.
Я делаю что-то очень неправильно, или FindBugs слишком нетерпелив?
Другой способ сформулировать этот вопрос:каково правильное поведение, если объект, переданный в Equals, не того же типа, что и LHS:Это неверно или должно быть исключение?
Например:
public boolean equals(Object rhs)
{
MyType rhsMyType = (MyType)rhs; // Should throw exception
if(this.field1().equals(rhsMyType.field1())... // Or whatever
}
Решение
Обычно при реализации равенства вы можете проверить, равен ли (или совместим) класс аргумента реализующему классу, прежде чем приводить его.Что-то вроде этого:
if (getClass() != obj.getClass())
return false;
MyObj myObj = (MyObj) obj;
Если вы сделаете это таким образом, вы предотвратите появление предупреждения FindBugs.
Примечание к комментарию:
Некоторые люди утверждают, что следует использовать instanceof
вместо getClass
для проверки безопасности типов.По этому поводу идет большая дискуссия, в которую я старался не вдаваться, когда заметил, что вы можете проверить равенство классов. или совместимость, но я думаю, я не могу избежать этого.Это сводится к следующему: если вы используете instanceof
вы можете поддерживать равенство между экземплярами класса и экземплярами его подкласса, но вы рискуете нарушить симметричный контракт equals
.Вообще я бы рекомендовал не использовать instanceof
если только вы не знаете, что вам это нужно, и не знаете, что делаете.Для получения дополнительной информации см.:
- http://www.artima.com/weblogs/viewpost.jsp?thread=4744
- Какие проблемы следует учитывать при переопределении равенства и hashCode в Java?
- http://www.macchiato.com/columns/Durable5.html
- http://commons.apache.org/lang/api-release/org/apache/commons/lang/builder/EqualsBuilder.html (Помощник по реализации Apache common)
- http://www.eclipsezone.com/eclipse/forums/t92613.rhtml (По умолчанию Eclipse равен генератору)
- Генератор NetBeans также использует getClass().
Другие советы
Вероятно, вы делаете что-то вроде этого:
public class Foo {
// some code
public void equals(Object o) {
Foo other = (Foo) o;
// the real equals code
}
}
В этом примере вы предполагаете что-то относительно аргумента методаquals():Вы предполагаете, что это тип 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
).
Я бы рекомендовал игнорировать указанное предупреждение findbugs.На практике, если метод Equals вызывается с объектом неожиданного класса, это почти наверняка ошибка, и вы хотите быстро устранить ошибки.
Например, если у вас есть «Файлы ArrayList» и вы вызываете files.contains («MyFile.txt»), было бы неплохо, если бы вы получили исключение ClassCastException.Вместо этого Java просто возвращает false, и, вероятно, пройдет много времени, прежде чем вы обнаружите эту ошибку.
Я начинаю свои реализации равенства (Объекта) следующим образом:
if ((object == null) || !(object instaceof ThisClass)) {
return false;
}
Это также предотвратит появление предупреждения FindBugs, но не приведет к автоматическому возврату. false
когда передается подкласс ThisClass.Его также можно было бы считать равным, особенно если equals(Object)
метод не был переопределен.