覆盖java equals()方法 – 不工作?
我今天用equals()
方法遇到了一个有趣(而且非常令人沮丧)的问题,这导致了我认为是经过良好测试的类崩溃并导致我花了很长时间才能追踪的错误。
为了完整起见,我没有使用IDE或调试器 – 只是老式的文本编辑器和System.out。 时间非常有限,这是一个学校项目。
无论如何 –
我正在开发一个基本的购物车,它可以包含Book
对象的ArrayList
。 为了实现addBook()
, removeBook()
和hasBook()
方法,我想检查Book
已存在Book
。 我走了 –
public boolean equals(Book b) { ... // More code here - null checks if (b.getID() == this.getID()) return true; else return false; }
一切都在测试中运行良好。 我创建了6个对象并用数据填充它们。 在Cart
上做了很多添加,删除,has()操作,一切正常。 我读到你可以有equals(TYPE var)
或equals(Object o) { (CAST) var }
但是假设它既然有效,也没关系太多。
然后我遇到了一个问题 – 我需要在Book类中创建一个只有 ID
的Book
对象。 不会输入任何其他数据。 基本上如下:
public boolean hasBook(int i) { Book b = new Book(i); return hasBook(b); } public boolean hasBook(Book b) { // .. more code here return this.books.contains(b); }
突然之间, equals(Book b)
方法不再有效。 这花了很长时间没有一个好的调试器跟踪,并假设Cart
类已经过适当的测试和正确。 将equals()
方法交换到以下内容后:
public boolean equals(Object o) { Book b = (Book) o; ... // The rest goes here }
一切都开始了。 是否有理由该方法决定不采用Book参数,即使它显然是 Book
对象? 唯一的区别似乎是它是在同一个类中实例化的,并且只填充了一个数据成员。 我非常困惑。 请揭开一些光芒?
在Java中,从Object
inheritance的equals()
方法是:
public boolean equals(Object other);
换句话说,参数必须是Object
类型。
ArrayList
使用正确的equals方法,在那里你总是调用那个没有正确覆盖Object
的equals的方法。
不正确覆盖该方法可能会导致问题。
我每次都重写等于以下内容:
@Override public boolean equals(Object other){ if (other == null) return false; if (other == this) return true; if (!(other instanceof MyClass))return false; MyClass otherMyClass = (MyClass)other; ...test other properties here... }
使用@Override
注释可以帮助实现愚蠢的错误。
只要您认为自己覆盖了超类或接口的方法,就可以使用它。 这样,如果你做错了,你将收到编译错误。
如果您使用eclipse,请转到顶部菜单
Source – >生成equals()和hashCode()
对你的问题稍微偏离主题,但无论如何它可能值得一提:
Commons Lang有一些很好的方法可以用来覆盖equals和hashcode。 查看EqualsBuilder.reflectionEquals(…)和HashCodeBuilder.reflectionHashCode(…) 。 过去让我感到很头疼 – 虽然当然如果你只想对ID做“等于”它可能不适合你的情况。
我同意你应该在覆盖equals(或任何其他方法)时使用@Override
注释。
保存样板代码的另一个快速解决方案是Lombok EqualsAndHashCode注释 。 它简单,优雅,可定制。 并不取决于IDE 。 例如;
import lombok.EqualsAndHashCode; @EqualsAndHashCode(of={"errorNumber","messageCode"}) // Will only use this fields to generate equals. public class ErrorMessage{ private long errorNumber; private int numberOfParameters; private Level loggingLevel; private String messageCode;
请参阅avaliable 选项以自定义在equals中使用的字段。 龙目岛在maven中是可用的。 只需添加提供的范围:
org.projectlombok lombok 1.14.8 provided
在Android Studio中是alt + insert —> equals和hashCode
例:
@Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Proveedor proveedor = (Proveedor) o; return getId() == proveedor.getId(); } @Override public int hashCode() { return getId(); }
考虑:
Object obj = new Book(); obj.equals("hi"); // Oh noes! What happens now? Can't call it with a String that isn't a Book...
instanceOf
语句通常用于equals的实现。
这是一个流行的陷阱!
问题是使用instanceOf
违反了对称规则:
(object1.equals(object2) == true)
当且仅当 (object2.equals(object1))
如果第一个equals为true,而object2是obj1所属类的子类的实例,那么第二个equals将返回false!
如果ob1所属的被认定的类被声明为final,那么这个问题就不会出现,但一般来说,你应该测试如下:
this.getClass() != otherObject.getClass();
如果没有,则返回false,否则测试字段以进行相等性比较!
recordId是对象的属性
@Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Nai_record other = (Nai_record) obj; if (recordId == null) { if (other.recordId != null) return false; } else if (!recordId.equals(other.recordId)) return false; return true; }