基类Object 的方法public boolean equals(Object obj) 以下内容来自于effective java
1)当一个新类没有覆写equals方法,并且其超类亦没有覆写equals时。equals方法的返回值与==相同
2)确信本类的equals方法不会被调用(内部的private类或包内可见的类),为了确实确保,可以这样
@Override public boolean equals(Object o) {
throw new AssertionError(); // Method is never called
}
3)当一个类具有逻辑相等(logical equality)功能的要求,而其超类并没有提供相关功能的覆写。这便是覆写equals方法的时机了。
4)enum类一般不用覆写,因为其保证了,逻辑相等的对象肯定是内存中的同一个对象
5)contract :对于非空实例,要求自反性,对称性,传递性。和多次调用的一致性,还有 非空对象o,o.equals(null)返回false
6)违反自反性,你向集合中加入一个元素,contains方法会报告说集合中没有这个元素。
一个例子:
public class Point {
private final int x;
private final int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
@Override public boolean equals(Object o) {
if (!(o instanceof Point))
return false;
Point p = (Point)o;
return p.x == x && p.y == y;
}
... // Remainder omitted
}
public class ColorPoint extends Point {
private final Color color;
public ColorPoint(int x, int y, Color color) {
super(x, y);
this.color = color;
}
... // Remainder omitted
}
一。如果子类这么写,那么违反了对称性(基类对象与子类对象之间),满足传递性
@Override public boolean equals(Object o) {
if (!(o instanceof ColorPoint))
return false;
return super.equals(o) && ((ColorPoint) o).color == color;
}
二 如果改一下,满足对称性,那么却违返了传递性
@Override public boolean equals(Object o) {
if (!(o instanceof Point))
return false;
// If o is a normal Point, do a color-blind comparison
if (!(o instanceof ColorPoint))
return o.equals(this);
// o is a ColorPoint; do a full comparison
return super.equals(o) && ((ColorPoint)o).color == color;
}
子类绿点euqals基类无颜色同一个点,equals子类红点,但很显然坐标相同的红点和绿点不等
三如果在基类中就这样写,那么违反了 Liskov substitution principle (里式转换原则即任何子类对象可以当做基类的
对象)
@Override public boolean equals(Object o) {
if (o == null || o.getClass() != getClass())
return false;
Point p = (Point) o;
return p.x == x && p.y == y;
}
结论: There is no way to extend an instantiable class and add a value component while preserving the equals contract
(没有办法在扩展一个类,加入一个字段后,仍然保持equals协议
办法:改继承为组合,代码如下
public class ColorPoint {
private final Point point;
private final Color color;
public ColorPoint(int x, int y, Color color) {
if (color == null)
throw new NullPointerException();
point = new Point(x, y);
this.color = color;
}
/**
* Returns the point-view of this color point.
*/
public Point asPoint() {
return point;
}
@Override public boolean equals(Object o) {
if (!(o instanceof ColorPoint))
return false;
ColorPoint cp = (ColorPoint) o;
return cp.point.equals(point) && cp.color.equals(color);
}
... // Remainder omitted
}
7)java.sql.Timestamp继承 java.util.Date 但equals实现时违反了对称性
8)如果基类是abstract的或interface等不可实例化的类,那么便不存在这样的问题 了
9)一些建 议 一:首先测试参数是不是指向本身,这个用==实现,可以提高性能,如果比较很占用资源的话
二:用instanceof关键字,同时解决参数为null的问题
三:转换类型,并比较每一个重要的字段.如果字段不是float或double,用==即可,对于接口,可能要使用
其中的方法,对于类,可能要递归调用.对于float和double用Float.compare或Double.compare(这是因为NaN或类似物的存在)
对于数组可以使用 Arrays.equals(since 1.5)
四:对于可能含有null的字段(field == null ? o.field == null : field.equals(o.field))
五:从性能考虑,先比较最可能不同的字段,最少花费即可比较的字段
10)当完成时,问自己是否,对称性,传递性,一致性
11)注意方法的签名
Don’t substitute another type for Object in the equals declaration. It is not
uncommon for a programmer to write an equals method that looks like this,
and then spend hours puzzling over why it doesn’t work properly:
public boolean equals(MyClass o) {
...
}