重写java equals()方法的怪癖
我遇到了一个有趣的(非常令人沮丧的)问题,就是今天的equals()方法导致了我认为是一个经过良好testing的类崩溃,并导致了一个我花了很长时间追踪的bug。
为了完整起见,我没有使用IDE或debugging器 – 只是老式的文本编辑器和System.out的。 时间非常有限,这是一个学校项目。
无论如何 –
我正在开发一个可以包含Book对象的ArrayList的基本购物车。 为了实现Cart的addBook(),removeBook()和hasBook()方法,我想检查Book是否已经存在于购物车中。 所以我走了 –
public boolean equals(Book b) { ... // More code here - null checks if (b.getID() == this.getID()) return true; else return false; }
所有的testing都很好。 我创build了6个对象并填充数据。 做很多添加,删除,有()操作购物车,一切工作正常。 我读过,你可以有等于(TYPE var)或等于(Object o){(CAST)var},但是假设既然它在工作,那么没有太大的关系。
然后我碰到一个问题 – 我需要在Book类中创build一个Book对象,只有它的ID。 没有其他数据将被input到它。 基本上如下:
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); }
突然之间,平等(书b)方法不再起作用。 这花了很长时间追踪没有一个好的debugging器,并假设购物车类正确testing和正确。 在将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
types的。
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,只需转到顶层菜单
源 – >生成equals()和hashCode()
对于你的问题有点偏离主题,但是无论如何也许值得一提:
Commons Lang有一些很好的方法可以用来覆盖equals和hashcode。 查看EqualsBuilder.reflectionEquals(…)和HashCodeBuilder.reflectionHashCode(…) 。 过去让我头痛不已 – 当然,如果你只是想在身份证上做“平等”,它可能不适合你的情况。
我也同意,你应该使用@Override
注释,只要你重写equals(或任何其他方法)。
另一个节省样板代码的快速解决scheme是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;
查看可用的选项来自定义在等号中使用的字段。 龙目岛是可爱的在maven 。 只需添加它提供的范围:
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.14.8</version> <scope>provided</scope> </dependency>
instanceOf
语句通常用于实现equals。
这是一个stream行的陷阱!
问题是使用instanceOf
违反了对称性的规则:
(object1.equals(object2) == true)
if and only if (object2.equals(object1))
如果第一个equals为true,而object2是obj1所属类的子类的一个实例,则第二个equals将返回false!
如果ob1所属的类被声明为final,那么这个问题就不会出现,但总的来说,你应该testing如下:
this.getClass() != otherObject.getClass();
如果没有,则返回false,否则testing字段比较是否相等!
在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...
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; }