读书人

Java 相等性考虑

发布时间: 2013-01-26 13:47:02 作者: rapoo

Java 相等性思考

<programing in scala> 书中第28章讨论了对象相等性的问题,我觉得很有启发性。在这里从java语言的角度去解释,既为了自己能理解多一些,也可能对别人也有帮助。

?

相等性有以下的特点:

1. 自反的,即对任何非空的x,x.equals(x) 返回true

2. 对称, 即对于任何非空的x和y, x.equals(y) 当且仅当y.equals(x)返回true的时候返回true

3. 传递性, 即对于任何非空的x、y、z, x.equals(y) 返回true,且y.equals(z)返回true,则x.equals(z)返回true;

4. 对任何非空值x, x.equals(null)应返回false.

?

?

以下是一段典型的实现java 中euqlas,hashcode方法的代码

/** *  */package com.me.test;import java.util.HashSet;import java.util.Set;/** * @author Blues * */public class PointApp1 {/** * @param args */public static void main(String[] args) {Point a = new Point(1, 2);Point b = new ColoredPoint(1, 2, "red");Set<Point> points = new HashSet<Point>();points.add(a);System.out.println("set contains " + a + "?" + points.contains(a));System.out.println("set contains " + b + "?" + points.contains(b));//print false as b's Class is not Point.Point c = new Point(1, 1) {{this.setY(2);}};System.out.println(a);//Point [x=1, y=2]System.out.println(c);//Point [x=1, y=2]System.out.println("set contains " + c + "?" + points.contains(c));//false}}class Point {private int x;private int y;public Point(int x, int y) {super();this.x = x;this.y = y;}//leave it only for child.protected void setX(int x) {this.x = x;}protected void setY(int y) {this.y = y;}@Overridepublic int hashCode() {final int prime = 31;int result = 1;result = prime * result + x;result = prime * result + y;return result;}@Overridepublic boolean equals(Object obj) {if (this == obj)return true;if (obj == null)return false;if (getClass() != obj.getClass())return false;Point other = (Point) obj;if (x != other.x)return false;if (y != other.y)return false;return true;}@Overridepublic String toString() {return "Point [x=" + x + ", y=" + y + "]";}}class ColoredPoint extends Point {private String color;public ColoredPoint(int x, int y, String color) {super(x, y);this.color = color;}@Overridepublic int hashCode() {final int prime = 31;int result = super.hashCode();result = prime * result + ((color == null) ? 0 : color.hashCode());return result;}@Overridepublic boolean equals(Object obj) {if (this == obj)return true;if (!super.equals(obj))return false;if (getClass() != obj.getClass())return false;ColoredPoint other = (ColoredPoint) obj;if (color == null) {if (other.color != null)return false;} else if (!color.equals(other.color))return false;return true;}@Overridepublic String toString() {return "ColoredPoint [color=" + color + ", extends "+ super.toString() + "]";}}

?程序运行的结果:

set contains Point [x=1, y=2]?trueset contains ColoredPoint [color=red, extends Point [x=1, y=2]]?falsePoint [x=1, y=2]Point [x=1, y=2]set contains Point [x=1, y=2]?false

?首先这样的实现是正确的,它满足上面提到的自反,对称,传递等性质。但是,考虑一下Point c,它的类型是继承了Point的匿名类,但它仅仅是为了修改y坐标,所以从逻辑上面来说,Point a和Point c都是在坐标系里面,且不带其他属性的(比如颜色)点(1, 2);所以认为它们相等也有一定的道理;

当然前提是,我们并不希望ColoredPoint b也和a相等;

?

在书中作者提到了一种方式; 前面的实现解决不了a和c相等的问题,是因为equals是比较两个对象静态的类,所以,如果能在运行时判断两个对象是否(可以)相等,就可以解决问题;

具体的实现如下:

/** *  */package com.me.test;import java.util.HashSet;import java.util.Set;/** * @author Blues * */public class PointApp2 {/** * @param args */public static void main(String[] args) {Point a = new Point(1, 2);Point b = new ColoredPoint(1, 2, "red");Set<Point> points = new HashSet<Point>();points.add(a);System.out.println("set contains " + a + "?" + points.contains(a));System.out.println("set contains " + b + "?" + points.contains(b));//print false as b's Class is not Point.Point c = new Point(1, 1) {{this.setY(2);}};System.out.println(a);//Point [x=1, y=2]System.out.println(c);//Point [x=1, y=2]System.out.println("set contains " + c + "?" + points.contains(c));//true}static class Point {private int x;private int y;public Point(int x, int y) {super();this.x = x;this.y = y;}//leave it only for child.protected void setX(int x) {this.x = x;}protected void setY(int y) {this.y = y;}@Overridepublic int hashCode() {final int prime = 31;int result = 1;result = prime * result + x;result = prime * result + y;return result;}@Overridepublic boolean equals(Object obj) {if (this == obj)return true;if (obj == null)return false;//if (getClass() != obj.getClass())//return false;Point other = (Point) obj;if(!other.canEquals(this)) {return false;}if (x != other.x)return false;if (y != other.y)return false;return true;}@Overridepublic String toString() {return "Point [x=" + x + ", y=" + y + "]";}protected boolean canEquals(Object obj) {return obj instanceof Point;}}static class ColoredPoint extends Point {private String color;public ColoredPoint(int x, int y, String color) {super(x, y);this.color = color;}@Overridepublic int hashCode() {final int prime = 31;int result = super.hashCode();result = prime * result + ((color == null) ? 0 : color.hashCode());return result;}@Overridepublic boolean equals(Object obj) {if (this == obj)return true;if (!super.equals(obj))return false;//if (getClass() != obj.getClass())//return false;ColoredPoint other = (ColoredPoint) obj;if(!other.canEquals(this)) {return false;}if (color == null) {if (other.color != null)return false;} else if (!color.equals(other.color))return false;return true;}@Overridepublic String toString() {return "ColoredPoint [color=" + color + ", extends "+ super.toString() + "]";}@Overrideprotected boolean canEquals(Object obj) {return obj instanceof ColoredPoint;}}}

?运行结果:

?

set contains Point [x=1, y=2]?trueset contains ColoredPoint [color=red, extends Point [x=1, y=2]]?falsePoint [x=1, y=2]Point [x=1, y=2]set contains Point [x=1, y=2]?true

?

在Point类里面定义的一个canEquals 方法:当obj为Point的时候返回true;并且在equals方法里用?

!that.canEquals(this)

?代替

this.getClass() != that.getClass()

?这里顺序很重要,必须要由that来作为caller,以保证对称性;

?

因为ColoredPoint override了canEquals,并且只在obj为ColoredPoint时才返回true,所以a和b是不相等的;但是匿名Point类没有覆盖这个类,所以a和c是相等的;

?

这个例子也许比较极端,很少会实用匿名Point,并且恰好又需要相等性判断;所以典型的相等性实现在大部分情况下都是可用的。

?

但是如果换个角度考虑,如果想要 ColoredPoint b 和a相等呢;虽然ColoredPoint多了个颜色属性,但在坐标系里面是同一个点;那么这种方法就可以很方便的修改,只需要不覆盖canEquals方法即可,而不用修改Point类的实现。 (但是不推荐这样做,因为这样实现ColoredPoint将违背传递性要求,考虑另外一个ColoredPoint d,坐标也为(1,2),但颜色是green,那么a equals b, a equals d, 但是b !equals d; 所以这里只是提到了一个潜在的优势)

?

?

另外值得一提的是,用scala来写这个例子要简短很多。

?

?

读书人网 >编程

热点推荐