我今天用equals()
方法遇到了一个有趣(而且非常令人沮丧)的问题,这导致了我认为经过严格测试的类崩溃并导致我花了很长时间才能找到的错误。
为了完整起见,我没有使用IDE或调试器 - 只是老式的文本编辑器和System.out。时间非常有限,这是一个学校项目。
无论如何 -
我正在开发一个基本购物车,其中可能包含 ArrayList
Book
个对象。为了实现购物车的addBook()
,removeBook()
和hasBook()
方法,我想检查Book
中是否已存在Cart
。我走了 -
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类中创建一个只有 Book
的{{1}}对象。不会输入任何其他数据。基本上如下:
ID
突然之间,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()
一切都开始了。是否有理由该方法决定不采用Book参数,即使它是 public boolean equals(Object o) {
Book b = (Book) o;
... // The rest goes here
}
对象?唯一的区别似乎是它是在同一个类中实例化的,并且只填充了一个数据成员。我很困惑。请揭开一些亮点?
答案 0 :(得分:321)
在Java中,从equals()
继承的Object
方法是:
public boolean equals(Object other);
换句话说,参数必须是Object
类型。
ArrayList
使用正确的equals方法,在那里你总是调用那个没有正确覆盖Object
等于的方法。
不正确地覆盖方法会导致问题。
我每次都覆盖等于以下内容:
@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
注释可以帮助实现愚蠢的错误。
只要您认为自己覆盖了超级类或接口的方法,就可以使用它。这样,如果你做错了,你就会遇到编译错误。
答案 1 :(得分:107)
如果您使用eclipse,请转到顶部菜单
来源 - >生成equals()和 hashCode()方法
答案 2 :(得分:11)
对你的问题稍微偏离主题,但无论如何它可能值得一提:
Commons Lang有一些很好的方法可以用来覆盖equals和hashcode。查看EqualsBuilder.reflectionEquals(...)和HashCodeBuilder.reflectionHashCode(...)。过去让我感到很头疼 - 虽然当然如果你只是想对ID做“等于”它可能不适合你的情况。
我同意你应该在覆盖equals(或任何其他方法)时使用@Override
注释。
答案 3 :(得分:4)
另一种节省样板代码的快速解决方案是Lombok EqualsAndHashCode annotation。它简单,优雅,可定制。并且不依赖于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;
请参阅options avaliable以自定义在equals中使用的字段。龙目岛在maven中是可用的。只需使用提供的范围添加它:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.14.8</version>
<scope>provided</scope>
</dependency>
答案 4 :(得分:1)
是 alt + insert ---&gt; 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();
}
答案 5 :(得分:1)
考虑:
Object obj = new Book();
obj.equals("hi");
// Oh noes! What happens now? Can't call it with a String that isn't a Book...
答案 6 :(得分:0)
instanceOf
语句通常用于实现equals。
这是一个很受欢迎的陷阱!
问题是使用instanceOf
违反了对称规则:
(object1.equals(object2) == true)
当且仅当 (object2.equals(object1))
如果第一个equals为true,则object2是子类的实例 obj1所属的类,那么第二个等于将返回false!
如果ob1所属的被认定的类被声明为final,那么这个 问题不会出现,但一般来说,你应该测试如下:
this.getClass() != otherObject.getClass();
如果没有,则返回false,否则测试
要比较相等的字段!