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对象替换为其他对象.

浙公网安备 33010602011771号