effective java学习之对于所有对象都通用的方法——覆盖equals时请遵守通用约定

Object有哪些公用方法?

  答:Clone,getClas,toString,finalize,equals,hashCode,wai,notify,notifyAll方法。

不复写equals(即直接用Object#equals)

  1.类的每个实例本质上都是唯一的(针对代表活动实体而不是值的类);

  2.不关心是否提供了逻辑相等的功能;

  3.超类覆盖的equals,通过继承过来的行为对子类也是合适的;

  4.类是私有或包级私有的,可以确定其equals永远不会被调用,可通过如下方式强化约束。

注意:当类具有自己特有的逻辑相等概念时,就需要重写equals方法。

  单例类的逻辑相同和对象等同是一回事.

equals通用约定

  1.自反性:

     对于非null的引用值x,x.equals(x)必须返回true

  2.对称性

     对于非null的引用值x,y,当且仅当 y.equal(x)==true时,x.equals(y)==true;

代码示例:

public final class StringIgnoreCase {
    private final String params;
    public StringIgnoreCase(String params) {
        if(params==null)
            throw new NullPointerException();
        this.params=params;
    }
    @Override
    public boolean equals(Object obj) {
        if(obj instanceof StringIgnoreCase)
            return params.equalsIgnoreCase(((StringIgnoreCase) obj).params);
        if(obj instanceof String)
            return params.equalsIgnoreCase((String)obj);
        
        return false;
    }
}

测试:

public class Test {
    public static void main(String[] args) {
        StringIgnoreCase sic=new StringIgnoreCase("lowi");
        String sic2="LOWI";
        
        System.out.println("sic1 == sic2 ?"+sic.equals(sic2));
        System.out.println("sic2 == sic1 ?"+sic2.equals(sic));
        
        System.out.println("sic1 == sic1 ?"+sic.equals(sic));
        System.out.println("sic1 == sic2 ?"+sic2.equals(sic2));
        }
}

结果:
  sic1 == sic2 ?true
  sic2 == sic1 ?false,string调用的equal是自带的方式,并没有忽略字符串大小写
  sic1 == sic1 ?true
  sic1 == sic2 ?true

代码分析:

  sic.equals(sic2)的时候,是因为ic是StringIgnoreCase类型的,它会执行到上面的代码,而sic2是String类型的,sic2.equals(sic)的比较自然是String中的equals方法。

3.传递性

  对于任何非null的引用值x,y,z,如果x.equals(y)== true且y.equals(z)==true,则x.equals(z) == true.在扩展子类的时候尤其需要注意.

代码示例:

  父类point:

public 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;
    }
}

子类ColorPoint:

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;
    }
    @Override
    public boolean equals(Object obj) {
        if(!(obj instanceof ColorPoint)){
            return false;
        }
        ColorPoint cpt=(ColorPoint) obj;
        return cpt.point.equals(point) && cpt.color.equals(color);
    }
}

测试代码:

public class Test {
    public static void main(String[] args) {
        ColorPoint cp1=new ColorPoint(1,2,Color.BLACK);
        ColorPoint cp2=new ColorPoint(1,2,Color.BLACK);
        ColorPoint cp3=new ColorPoint(1,2,Color.BLUE);
        System.out.println("cp1==cp2?"+cp1.equals(cp2));
        System.out.println("cp1==cp3?"+cp1.equals(cp3));
        System.out.println("cp3==cp2?"+cp3.equals(cp1));
    }
}

结果:

  cp1==cp2?true
  cp1==cp3?false
  cp3==cp2?false

4.一致性

  对于非null的引用值x,y,只要equals比较操作在对象中所用的信息没有被修改,则多次调用equals方法返回值也应一致.

5.非空性

  对于任何非null的引用值x,x.equals(null) == false

高质量equals方法的诀窍:

  1.使用==操作符检查——“参数是否为这个对象的引用”;

  2.使用instanceof检查——“参数是否为正确的类型”;

  3.参数转换成正确的类型;

  4.对于每个关键域,检查参数中的域是否与对象的域相匹配;

  5.写完后,问自己:是否对称、是否传递、是否一致。

代码:

class PhoneNumber {
    private final int areaCode;
    private final int prefix;
    private final int lineNumber;

    public PhoneNumber(int areaCode, int prefix, int lineNumber) {
        this.areaCode = areaCode;
        this.prefix = prefix;
        this.lineNumber = lineNumber;
    }
    @Override
    public boolean equals(Object obj) {
        // 使用==操作符检查参数是否为这个对象的引用
        if (obj == this) {
            return true;
        }
        // 2、使用instanceof操作符检查参数是否为正确的类型
        if (!(obj instanceof PhoneNumber)) {
            return false;
        }
        // 3、把参数转换成正确的类型
        PhoneNumber pn = (PhoneNumber)obj;
        // 4、应该问自己三个问题,它是否是对称的、传递的、一致的。
        return pn.lineNumber == lineNumber
                && pn.prefix == prefix
                && pn.areaCode == areaCode;
    }   
}

告诫

  1.复写 equals时总要复写hashcode

  2.不要企图让equals过于只能(即考虑所有值域情况)

  3.不要将声明中的Object对象替换为其他对象.

posted @ 2018-04-22 00:19  小码农成长记  阅读(157)  评论(0)    收藏  举报