前言 :
在Java中,如果要比較兩個物件的實質相等性,並不是使用==,而是必須透過equals()方法,例如 :
- String s1 = new String("Java");
- String s2 = new String("Java");
- System.out.println(s1 == s2); // 顯示 false
- System.out.println(s1.equals(s2)); // 顯示 true
物件相等性 :
如果你定義類別時,沒有重新定義equals()方法,則預設繼承自Object,Object的equals()方法是定義為 :
- public boolean equals(Object obj) {
- return (this == obj);
- }
- public class Point {
- public final int x;
- public final int y;
- public Point(int x, int y) {
- this.x = x;
- this.y = y;
- }
- public boolean equals(Point that) {
- return this.x == that.x && this.y == that.y;
- }
- }
- Point p1 = new Point(1, 1);
- Point p2 = new Point(1, 1);
- System.out.println(p1.equals(p2)); // 顯示 true
- Point p1 = new Point(1, 1);
- Point p2 = new Point(1, 1);
- Set
pSet = new HashSet (); - pSet.add(p1);
- System.out.println(pSet.contains(p2)); // 顯示 false
- Object p1 = new Point(1, 1); // 使用 Object 指標, 所以之後的 p1.equals() 版本是 Object 版本
- Point p2 = new Point(1, 1);
- System.out.println(p1.equals(p2)); // 顯示 false
- public class Point {
- public final int x;
- public final int y;
- public Point(int x, int y) {
- this.x = x;
- this.y = y;
- }
- @Override
- public boolean equals(Object that) {
- if(that instanceof Point) {
- Point p = (Point) that;
- return this.x == p.x && this.y == p.y;
- }
- return false;
- }
- }
- Object p1 = new Point(1, 1);
- Point p2 = new Point(1, 1);
- System.out.println(p1.equals(p2)); // 顯示 true
- Point p1 = new Point(1, 1);
- Point p2 = new Point(1, 1);
- Set
pSet = new HashSet (); - pSet.add(p1);
- System.out.println(pSet.contains(p2)); // 可能顯示 false
以HashSet為例,會先使用hashCode()得出該將物件放至哪個雜湊桶(hash buckets)中,如果雜湊桶有物件,再進一步使用equals()確定實質相等性,從而確定Set中不會有重複的物件。上例中說可能會顯示false,是因為若湊巧物件hashCode()算出在同一個雜湊桶,再進一步用equals()就有可能出現true. 因此在重新定義equals()時,最好重新一併重新定義hashCode(). 例如 :
- public class Point {
- public final int x;
- public final int y;
- public Point(int x, int y) {
- this.x = x;
- this.y = y;
- }
- @Override
- public boolean equals(Object that) {
- if(that instanceof Point) {
- Point p = (Point) that;
- return this.x == p.x && this.y == p.y;
- }
- return false;
- }
- @Override
- public int hashCode() {
- return 41 * (41 + x) + y;
- }
- }
- Point p1 = new Point(1, 1);
- Point p2 = new Point(1, 1);
- Set
pSet = new HashSet (); - pSet.add(p1);
- System.out.println(pSet.contains(p2)); // 顯示 true, 使用 hashCode() 會得到同一個 hash bucket 再用 equals() 得到 true
- Point p1 = new Point(1, 1);
- Set
pSet = new HashSet (); - pSet.add(p1);
- System.out.println(pSet.contains(p1)); // 顯示 true
- p1.x = 2;
- System.out.println(pSet.contains(p1)); // 顯示 false
目前定義的Point,其equals()方法滿足以上幾個約定(你可以自行寫程式測試)。現在考慮繼承的情況,你要定義3D的點 :
- public class Point3D extends Point {
- public final int z;
- public Point3D(int x, int y, int z) {
- super(x, y);
- this.z = z;
- }
- @Override
- public boolean equals(Object that) {
- if(that instanceof Point3D) {
- Point3D p = (Point3D) that;
- return super.equals(p) && this.z == p.z;
- }
- return false;
- }
- }
- Point p1 = new Point(1, 1);
- Point p2 = new Point3D(1, 1, 1);
- System.out.println(p1.equals(p2)); // 顯示 true
- System.out.println(p2.equals(p1)); // 顯示 false
- public class Point3D extends Point {
- public final int z;
- public Point3D(int x, int y, int z) {
- super(x, y);
- this.z = z;
- }
- @Override
- public boolean equals(Object that) {
- if(that instanceof Point3D) {
- Point3D p = (Point3D) that;
- return super.equals(p) && this.z == p.z;
- }
- if(that instanceof Point) {
- return that.equals(this);
- }
- return false;
- }
- }
- Point p1 = new Point(1, 1);
- Point p2 = new Point3D(1, 1, 1);
- Point p3 = new Point3D(1, 1, 2);
- System.out.println(p2.equals(p1)); // 顯示 true
- System.out.println(p1.equals(p3)); // 顯示 true
- System.out.println(p2.equals(p3)); // 顯示 false
直接判斷類別,讓不同類別的實例視為不相等,就這個例子而言,使得Point只能與Point比,Point3D只能與Point3D比,直接解決了不同繼承階層下equals()的合約問題. 不過在以下這種需求時,這樣的定義也許不符合你的需求 :
- Point p1 = new Point(1, 1);
- Point p2 = new Point(1, 1) {
- @Override
- public String toString() {
- return "(" + x + ", " + y + ")";
- }
- };
- Set
pSet = new HashSet (); - pSet.add(p1);
- System.out.println(pSet.contains(p1)); // 顯示 true
- System.out.println(pSet.contains(p2)); // 顯示 false,但你想顯示 true, 因為 p2 是匿名類別!!!
- public class Point {
- public final int x;
- public final int y;
- public Point(int x, int y) {
- this.x = x;
- this.y = y;
- }
- @Override
- public boolean equals(Object that) {
- if(that instanceof Point) {
- Point p = (Point) that;
- return p.canEquals(this) &&
- this.x == p.x &&
- this.y == p.y;
- }
- return false;
- }
- public boolean canEquals(Object that) {
- return that instanceof Point;
- }
- @Override
- public int hashCode() {
- return 41 * (41 + x) + y;
- }
- }
- public class Point3D extends Point {
- public final int z;
- public Point3D(int x, int y, int z) {
- super(x, y);
- this.z = z;
- }
- @Override
- public boolean equals(Object that) {
- if(that instanceof Point3D) {
- Point3D p = (Point3D) that;
- return p.canEquals(this) &&
- super.equals(p) && this.z == p.z;
- }
- return false;
- }
- @Override
- public boolean canEquals(Object that) {
- return that instanceof Point3D;
- }
- @Override
- public int hashCode() {
- return 41 * super.hashCode() + z;
- }
- }
沒有留言:
張貼留言