您的位置:首页 > 移动开发 > Objective-C

【JAVA学习】EffectiveJava的学习笔记--第二章 Object类的通用方法

2014-09-08 16:23 393 查看
Object类就是为了扩展用,其中非final方法(equals hashCode,toString,Clone,finalize)耳熟能详的方法在改写的时候必须要有很多通用约定,否则其他依赖于这些约定的类就无法与这些类正常工作。

1.equals方法

1.1什么时候该改写equals方法?

当一个类有自己的逻辑相等(而非对象身份)的概念,超类也没有改写equals以实现自己需要的行为时候,一般都是都是比较两个指向值对象的引用的时候,希望知道逻辑是否相等,而非指向同一个对象。

1.2 实现改写的通用约定

自反性: x.equals(x) 为true
对称性: x.equals(y) 为true,则 y.equals(x)也为true

下面这段代码coment部分的如果存在的话就违反了对称性,即equals方法中CaseInsensitiveString认识String类因此进行了判断,而反过来String类却人不认识CaseInsensitiveString类。这种不自觉的错误经常不经意就发生。

public class CaseInsensitiveString {

private String s;
public CaseInsensitiveString(String s) {
if(s == null) {
throw new NullPointerException();
}
this.s = s;
}

public boolean equals(Object o) {
if(o instanceof CaseInsensitiveString) {
return  s.equalsIgnoreCase(((CaseInsensitiveString)o).s);
}
// 这段代码违反了对称性
//		if(o instanceof  String) {
//			return s.equalsIgnoreCase((String)o);
//		}
return false;
}

public static void main(String[] args) {
String s = "abc";
CaseInsensitiveString cis = new CaseInsensitiveString("Abc");
CaseInsensitiveString cis2 = new CaseInsensitiveString("ABC");
System.out.println("cis.equals(s)= " +cis.equals(s));
System.out.println("s.equals(cis) = " + s.equals(cis));
System.out.println("cis2.equals(cis) = " + cis2.equals(cis));
}

}

comment之前的结果是:

cis.equals(s)= true
s.equals(cis) = false
cis2.equals(cis) = true


comment段后的结果是:

cis.equals(s)= false
s.equals(cis) = false
cis2.equals(cis) = true


传递性: x.equals(y) 为true y.equals(z)为true, 则 x.equals(z)也为true

这个问题经常发生在继承扩展一个值的类的时候,如果继承扩展的类没有override equals则,调用equals的时候就会出现以下问题。

import java.awt.Color;
import java.awt.Point;

public class ColorPoint  extends Point{
private static final long serialVersionUID = 1L;
private Color  color;
public ColorPoint(int x, int y, Color color) {
super(x,y);
this.color = color;
}

public static void main(String[] args) {
// TODO Auto-generated method stub
Point p = new Point(1,2);
ColorPoint cp1 = new ColorPoint(1, 2, Color.red);
ColorPoint cp2 = new ColorPoint(1, 2, Color.black);
System.out.println("p.equals(cp1) =" + p.equals(cp1));
System.out.println("p.equals(cp2) =" + p.equals(cp2));
System.out.println("cp1.equals(cp2) =" + cp1.equals(cp2));
}

}


得到的结果是

p.equals(cp1) =true
p.equals(cp2) =true
cp1.equals(cp2) =true


但实际上cp1 和cp2是两个颜色不同的点。override equals方法后,虽然可以保证证对称性,但没有保证传递性,即p == p1 p==p2 得到的结果是p1 != p2.

@Override
public boolean equals(Object obj) {
// TODO Auto-generated method stub
if(!(obj instanceof Point)) {
return false;
}

// if obj is normal point  do a color-blind comparasion
if(!(obj instanceof ColorPoint)) {
return this.equals(obj);
}

ColorPoint cp = (ColorPoint)obj;
return super.equals(obj) && (cp.color == this.color);
}
得到的结果是:

p.equals(cp1) =true
p.equals(cp2) =true
cp1.equals(cp2) =false


为了解决这个传递性的问题,一般要扩展可实例化的类的同时,既要增加新的特征,又要保留equals的约定,没有办法做到。

另外一种解决方案是 采用【复合】的方式。

即ColorPoint不是继承与Point,而是该类包含了一个Point的成员变量。

非空性: 非空引用值 x x.equals(null) 则返回false,非空不用多说了,就是上面override里面的加上null 的判断。
对于类中每个关键域,检查实参中与当前对象中对应的域是否匹配。

1.3检查equals方法的一些小结:

如果该类的类型是接口,则通过接口访问实参中的关键域
如果该类的类型是类,则访问实参中的关键域。
对于不是float 也不是double则可以直接用==比较,对于对象引用域则可以用递归的equals方法,
对于float则使用Float.floatToIntBits转换成int类型的值,再==比较
对于double则使用Double.doubleToLongBits转换成long型的值,再==比较
对于数组域,则以应用到元素上,有些对象引用域包含null是合法的,所以为了避免null异常,可以通过判断该field是不是null然后再进行常规的equals比较。

1.4 修改equals的时候一定要修改hashcode方法,否则违反了相等的对象必须有相等的散列码。

这个下一篇再记述。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐