`
salever
  • 浏览: 250283 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Object的equals()重写

阅读更多
JDK1.6 API写道
public boolean equals(Object obj)指示其他某个对象是否与此对象“相等”。
equals 方法在非空对象引用上实现相等关系:

自反性:对于任何非空引用值 x,x.equals(x) 都应返回 true。
对称性:对于任何非空引用值 x 和 y,当且仅当 y.equals(x) 返回 true 时,x.equals(y) 才应返回 true。
传递性:对于任何非空引用值 x、y 和 z,如果 x.equals(y) 返回 true,并且 y.equals(z) 返回 true,那么 x.equals(z) 应返回 true。
一致性:对于任何非空引用值 x 和 y,多次调用 x.equals(y) 始终返回 true 或始终返回 false,前提是对象上 equals 比较中所用的信息没有被修改。
对于任何非空引用值 x,x.equals(null) 都应返回 false。
Object 类的 equals 方法实现对象上差别可能性最大的相等关系;即,对于任何非空引用值 x 和 y,当且仅当 x 和 y 引用同一个对象时,此方法才返回 true(x == y 具有值 true)。

注意:当此方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。


参数:
obj - 要与之比较的引用对象。
返回:
如果此对象与 obj 参数相同,则返回 true;否则返回 false。
另请参见:
hashCode(), Hashtable

 我们先来看看Object中equals的实现:

  public boolean equals(Object obj) {
	return (this == obj);
    }

 当且仅当两个对象是同一个时,equals才返回true,这个实现当然满足以上的自反性、对称性、传递性、一致性。然而在绝大多数情况下,这个默认实现都不好用,用户往往需要重写这个方法。一旦重写了这个方法,很可能潜在的破坏了以上约束,给程序带来潜在威胁。

 

看一个Effective java中给出的一个违反对称性的例子:

/**
 * Case-insensitive string. Case of the original string is preserved by
 * toString, but ignored in comparisons.
 */
public final class CaseInsensitiveString {
	private String s;

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

	// Broken - violates symmetry!
	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;
	}
	// Remainder omitted
}

 这个实现对于

CaseInsensitiveString cis = new CaseInsensitiveString("Polish");
String s = "polish"; 

 cis.equals(s) 为true,而s.equals(cis) 为false。如果出现下面的代码,结果是什么呢?

List list = new ArrayList();
list.add(cis);
list.contains(s) = ?

 到底list.contains(s)返回true还是false,这就说不清了。按照目前的JVM实现,这个是false。

一个可能的实现为:

	public boolean equals(Object o) {
		return o instanceof CaseInsensitiveString
				&& ((CaseInsensitiveString) o).s.equalsIgnoreCase(s);
	}

 

再看一个违反传递性的例子:

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

public class ColorPoint extends Point {
	private Color color;

	public ColorPoint(int x, int y, Color color) {
		super(x, y);
		this.color = color;
	}

	// Broken - violates transitivity.
	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;
	}
}

 根据上面的实现

ColorPoint p1 = new ColorPoint(1, 2, Color.RED);
Point p2 = new Point(1, 2);
ColorPoint p3 = new ColorPoint(1, 2, Color.BLUE);

 p1.equals(p2) = ture && p2.equals(p3) = true,而p1.equals(p3) = false,原因很简单,在

if (!(o instanceof ColorPoint))
	return o.equals(this);

这里出了问题,当o为Point时,调用了Point的equals(),忽略了color属性,从而使得p1.equals(p2)&&p2.equals(p3),一个可能的实现为

// Adds an aspect without violating the equals contract
public class ColorPoint {
private Point point;
private Color color;
public ColorPoint(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 ColorPoint))
return false;
ColorPoint cp = (ColorPoint)o;
return cp.point.equals(point) && cp.color.equals(color);
}
... // Remainder omitted
}

在重写扩展基类的equals方法时,最好放弃使用继承,而采取组成的方式,将基类作为一个成员属性,这样可以避免由继承带来的各种问题。There is simply no way to extend an instantiable class with a new aspect while preserving the compareTo contract。如果你想扩展一个已经实现了equals的类,不要去继承它,而是在类中使用一个它的实例对象作为成员。

 

 

接下来讲讲 == 操作符与equals()的关系。== 比较两个对象时,返回true意味着两个对象是同一个。在Object的默认实现中,== 与equals就是一回事,也就是

Object obj1, obj2;
obj1.equals(obj2) 与obj1 == obj2 是一样的效果,
但是要注意在obj1、obj2都为null时,前者会抛出异常而后者则返回true。

	Object obj1 = null, obj2 = null;
		if(obj1 == obj2){
			System.out.println("true");
		}else{
			System.out.println("false");
		}
		
		if(obj1.equals(obj2)){
			System.out.println("true");
		}else{
			System.out.println("false");
		}
	}

   输出为:

写道
true
Exception in thread "main" java.lang.NullPointerException

 

参考文档:

1,《Efficient Java》 Joshua Bloch

2, J2sk 6.0 API

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics