重写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中,从Objectinheritance的equals()方法是:

 public boolean equals(Object other); 

换句话说,参数必须是Objecttypes的。

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; }