1、何时需要重写equals
相信javaer们应该都知道equals方法,它是基类大佬Object中的一个方法,所以java下面所有的类都“自带”这个方法。看方法名就知道,意图就是对比传入的目标对象, 跟自己是否“相等”。我们先看看这个方法在Object类中的实现:
public boolean equals(Object obj) {
return (this == obj);
}
这个实现也算简单粗暴了,直接用“==”来跟目标对象作比较,这个意图就是:除非对方就是是自己本身,否则就不相等。 但就是因为这样的粗暴,造成限定得太死了。实际开发中,可能原生的equals不能满足业务的需求。所以需要重写,例如Integer中重写的equals方法,可以看到Integer并不强制目标对象是“自己本体”,而只是对比了被自己包装的int基本类型数值,这样的实现更加符合实际业务要求:
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();//对比被包装的整型数值
}
return false;
}
关于何时才需要重写equals方法,《Effective Java》中提到:
如果类具有自己特有的“逻辑相等”概念,而且超类还没有覆盖equals。这通常属于“值类”的情形。值类仅仅是一个表示值得类,例如Integer和String。程序员在利用equals方法来比较值对象的引用时,希望知道他们在逻辑上是否相等,而不是想了解它们是否指向同一个对象。
这句话翻译得有点拗口,不过意思还是明确的,何时才是需要重写equals,可以总结为以下两点:
- **该类只代表一个具体的“值“。**例如Double、Integer、Long等这种包装类型,其实这些类再怎么复杂,它们也只是代表一个数值,只要类型和数值相等,它们对象也理应是相等的。
- **业务逻辑相同的对象。**这个可以看作是第一点的延申,即对象间的成员变量、行为表现是一致的时候,也应该理解成是相同的对象。例如一个商品类Goods,只要它的商品ID,商品名称等关键信息是相等的,就是同一件商品。
2、重写equals方法要遵守的约定
2.1 重写equals错误示范
重写equals方法看似很简单,但是有很多重写的方式会导致意想不到的错误。举一个jdk里的一个错误例子,java.sql.Timestamp是继承自java.util.Date的,这两个类都分别重写了equals方法。我们可以来看看:
java.util.Date.equals:
public boolean equals(Object obj) {
return obj instanceof Date && getTime() == ((Date) obj).getTime();
}
java.sql.Timestamp:
public boolean equals(java