Item7:在改写equals时请遵循通用约定

 

/**

     *改写equals方法很容易导致严重的错误,如果满足下面任何一个条件,那么避免问题的最容易的方法

     *就是不改写equals方法,这样每个实例只与它自己相等

     *(1)一个类的每个实例本质上是唯一的。

     *(2)不关心一个类是否提供了逻辑相等的测试功能。

     *(3)超类改写了equals,从超类继承过来的行为对于子类也是合适的。

     *(4)一个类是私有的,或者是包级私有的,并且可以确定它的equals方法永远也不会被调用。

     *关于(4),为了以防万一有一天它被调用,最好以如下方式改写:

     *publicbooleanequals(Objecto){

     *thrownewUnsupportedOperationException();

     *}      

     */

    /**

     *Q:何时改写Object.equals呢?

     *A:当一个类有自己特有的逻辑相等概念,而且超类也没有改写equals以实现期望的行为时,

     *    这通常适用于值类(valueclass)的比较,如IntegerDate,这样做使得这个类的实例

     *    可以被用做映射表(map)的键(key),或者集合(set)的元素,并使mapset表现出预期的行为

     *注意:但是有一种值类——类型安全枚举类型不要求改写equals方法,因为它能保证每一个值至多只存

     *      在一个对象,所以Objectequals方法等同于逻辑意义上的equals方法

     */

    /**

     *Q:在改写equals方法时,遵守那些通用约定?

     *Aequals方法在非空对象引用上实现相等关系:

     *    (1)自反性(reflexive):对于任何非空引用值xx.equals(x)都应返回true

     *    (2)对称性(symmetric):对于任何非空引用值xy,当且仅当y.equals(x)返回true时,

     *       x.equals(y)也应返回true

     *    (3)传递性(transitive):对于任何非空引用值xyz,如果x.equals(y)返回true

     *       并且y.equals(z)返回true,那么x.equals(z)应返回true

     *    (4)一致性(consistent):对于任何非空引用值xy,多次调用x.equals(y)始终返回true或始终返回false

     *       前提是对象上equals比较中所用的信息没有被修改.

     *    (5)对于任何非空引用值xx.equals(null)都应返回false

     */

    如果违反以上的约定""""""""""""""""""""""""

    //(1)自反性(reflexive) 

    //如果想把一个类的实例加入到collection,该集合果断的告诉你,该集合不包含刚刚你加入的实例

    //(2)对称性(symmetric)

    /**

     *Case-insensitivestring.Caseoftheoriginalstringis

     *preservedbytoString,butignoredincomparisons.-Page27

     */

     publicfinalclass CaseInsensitiveString {

        private String s;

        public CaseInsensitiveString(String s) {

            if (s == null)

                thrownew NullPointerException();

            this.s = s;

        }

        //违反对称性,CaseInsensitiveStringequals方法知道普通的String对象,反之则不然

        /*

        public boolean equals(Object o) {

            if (o instanceof CaseInsensitiveString)

                return s.equalsIgnoreCase(

                    ((CaseInsensitiveString)o).s);

            if (o instanceof String) // One-way interoperability!

                return s.equalsIgnoreCase((String)o);

            return false;

        }

       */

        //修改后的

        publicboolean equals(Object o) {      

            return o instanceof CaseInsensitiveString &&

                ((CaseInsensitiveString)o).s.equalsIgnoreCase(s);        

        }

        //注意:改写equals时总是要改写hashCode

        publicstaticvoid main(String[] args) {

            CaseInsensitiveString cis = new CaseInsensitiveString("Polish");

            String s = "polish";

               

            System.out.println(cis.equals(s));

            System.out.println(s.equals(cis));

        }

    }


 

public class Color {

     private String name;

 

        private Color(String name) {

            this.name = name;

        }

 

        public static Color RED =   new Color("red");

        public static Color GREEN = new Color("red");

        public static Color BLUEnew Color("red");

}
 

//(3)传递性(transitive)

class Point {

   

    private final int x;

    private final int y;

    public Point(int x, int y) {

       this.x = x;

        this.y = y;

    }

    public boolean equals(Object o) {

       if (!(o instanceof Point))

           return false;

       Point p = (Point)o;

       return p.x == x && p.y == y;

    }

      

     //注意:改写equals时总是要改写hashCode

}

public class ColorPoint extends Point{

    private Color color;

 

    public ColorPoint(int x, int y, Color color) {

        super(x, y);

        this.color = color;

    }

 

 

    // 违反对称性,当比较一个普通点和一个有色点以及反过来的情况时,前者忽略颜色信息,后者总会返回false

    //如:Point p = new Point(1,2); ColorPoint cp = new ColorPoint(1,2,Color.RED);

    // p.equals(cp)返回truecp.equals(p)返回返回false;

    /*

    public boolean equals(Object o) {

        if (!(o instanceof ColorPoint))

            return false;

        ColorPoint cp = (ColorPoint)o;

        return super.equals(o) && cp.color == color;

    }

    */

 

    //修改后的,满足了对称性,违反了传递性

    //如:ColorPoint p1 = new ColorPoint(1,2,Color.RED);

    //Point p2 = new Point(1,2);

    //ColorPoint p3 = new ColorPoint(1,2,Color.Blue);

    //p1.equal(p2)p2.equals(p3)都返回true,但是p1.equals(p3)返回false

    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

        ColorPoint cp = (ColorPoint)o;

        return super.equals(o) && cp.color == color;

    }

 

    //注意:改写equals时总是要改写hashCode  

 

    //Entry

    public static void main(String[] args) {

      //对称性测试

        Point p = new Point(1, 2);

        ColorPoint cp = new ColorPoint(1, 2, Color.RED);

        System.out.println(p.equals(cp));

        System.out.println(cp.equals(p));

        System.out.println();

 

        // 传递性测试

        ColorPoint p1 = new ColorPoint(1, 2, Color.RED);

        Point p2 = new Point(1, 2);

        ColorPoint p3 = new ColorPoint(1, 2, Color.BLUE);

        System.out.println(p1.equals(p2));

        System.out.println(p2.equals(p3));

        System.out.println(p1.equals(p3));

    }

}

  

/**Q:如何解决在扩展一个可实例化类的同时,既要增加新的特征,同时保留equals约定?

 * A:没有一个简单的办法可以做到!!

 *   参照Item14:复合优于继承,这个问题还有有好的解决办法。

 * 注意:(1) java.sql.Timestampjava.util.Date进行了子类化,并且增加了nanosecond域,

 *    Timestampequals方法确实违反了对称性,如果混合使用DateTimestap对象,就可能出现不正确行为!!!

 *     (2)只要不可能创建超类的实例(如在抽象类的子类中增加新的特征),就不会违反equals方法的约定,

 */

public class NewColorPoint {

    private Point point;

    private Color color;

 

    public NewColorPoint(int x, int y, Color color) {

        point = new Point(x, y);

        this.color = color;

    }

 

   

   //Returns the point-view of this color point.   

    public Point asPoint() {

        return point;

    }

 

    public boolean equals(Object o) {

        if (!(o instanceof NewColorPoint))

            return false;

        NewColorPoint cp = (NewColorPoint)o;

        return cp.point.equals(point) && cp.color.equals(color);

    }

   

    //注意:改写equals时总是要改写hashCode

 

    public static void main(String[] args) {

     //对称性测试

        Point p = new Point(1, 2);

        NewColorPoint cp = new NewColorPoint(1, 2, Color.RED);

        System.out.println(p.equals(cp));

        System.out.println(cp.equals(p));

        System.out.println();

 

        // 传递性测试

        NewColorPoint p1 = new NewColorPoint(1, 2, Color.RED);

        Point p2 = new Point(1, 2);

        NewColorPoint p3 = new NewColorPoint(1, 2, Color.BLUE);

        System.out.println(p1.equals(p2));

        System.out.println(p2.equals(p3));

        System.out.println(p1.equals(p3));

    }

}

class Point {

   

    private final int x;

    private final int y;

    public Point(int x, int y) {

       this.x = x;

        this.y = y;

    }

    public boolean equals(Object o) {

       if (!(o instanceof Point))

           return false;

       Point p = (Point)o;

       return p.x == x && p.y == y;

    }

      

     //注意:改写equals时总是要改写hashCode

}

//(4)一致性,对于两个对象的对象,除非有一个对象(或两个都)被修改,那么它们必须始终相等

//      对于不可变对象,那么必须满足equals方法满足:相等的对象永远相等,不相等的对象永远不相等. 晕!

//(5)非空性,意思是所有的对象都不能等于null,通用约定不允许抛出NullPointerException

//          因此必须通过null测试:

//         public boolean(Object o){

//            if(o==null) return false; }

//         更好的方法:

//         public boolean equals(Object o){

//            if(!(o instanceof MyType)) return false;}

 

/**总之,要实现高质量的equals方法,应该:

 * (1)使用==操作符检查实参是否为指向对象的一个引用 (性能优化)

 * (2)使用instanceof操作符检查实参是否为正确的类型

 * (3)把实参转换到正确的类型

 * (4)对于该类中的每一个关键域,检查实参中的域与当前对象中对应的域值是否匹配

 * (5)编写完equals之后应该检查是否满足:对称性,传递性,一致性

 *

 * 改写equals方法的一些告诫

 * (1)当你改写equals的时候,总要改写hashCode

 * (2)不要企图让equals方法过于聪明,加入太多等价关系比较只会让事情变糟糕

 * (3)不要使equals方法依赖于不可靠的资源,否则将非常困难

 * (4)不要将equals声明中的Object对象替换为其他类型,即不要将equals(Object o)改为equals(MyClass c)

 *   该方法overload(重载)而不是override(改写)了Objectequals,

 *   只有它们返回同样的结果(但没必要)时才可以接受

 

 */

 

转载于:https://www.cnblogs.com/wxf0701/archive/2008/04/24/1169807.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值