基类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) {
...
}

解决方案 »

  1.   

    还有,重写equals(),一定要重写hasCode(),血泪的经验啊.
      

  2.   

    Java语言规范中规定equals方法应具有的特性:(1)自反性:对于任何非空引用,x.equals(x)应该返回true;(2)对称性:对于任何引用x和y,当且仅当y.equals(x)返回true,x.equals(y)也应该返回true;(3)传递性:对于任何引用x、y和z,如果x.equals(y)返回true,y.equals(z)返回true,x.equals(z)也应该返回true;(4)一致性:如果x和y引用的对象没有发生变化,反复调用x.equals(y)应该返回同样的结果;(5)对于任意非空引用x,x.equals(null)应该返回false。