漫谈equals方法(欢迎讨论)

来源:互联网 发布:淘宝有门票属性的 编辑:程序博客网 时间:2024/06/05 07:52

 

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

...

}