在 物件相等性 討論過如何重新定義equals()方法,如果定義類別時使用了泛型,則有幾個地方要注意的,例如:
import java.util.*;
class Basket<T> { T[] things; Basket(T... things) { this.things = things; } @Override public boolean equals(Object o) { if(o instanceof Basket<T>) { // 編譯錯誤 Basket that = (Basket) o; return Arrays.deepEquals(this.things, that.things); } return false; } } 如果你編譯這個程式,會發現以下的錯誤訊息:
illegal generic type for instanceof if(o instanceof Basket<T>) {
在程式中instanceof對Basket<T>的型態判斷是不合法的,因為Java的泛型所採用的是型態抹除,模式比對時,僅比對Basket型態,不會
針對當中的泛型真正之型態進行比對。
如果想要通過編譯,可以使用型態通配字元?:
import java.util.*;
class Basket<T> { T[] things; Basket(T... things) { this.things = things; } @Override public boolean equals(Object o) { if(o instanceof Basket<?>) { Basket that = (Basket) o; return Arrays.deepEquals(this.things, that.things); } return false; } } 現在你可以使用equals()來比較兩個Basket是不是相同了:
public class Main { public static void main(String[] args) { Basket<Integer> b1 = new Basket<Integer>(1, 2); Basket<Integer> b2 = new Basket<Integer>(1, 2); Basket<Integer> b3 = new Basket<Integer>(2, 2); Basket<String> b4 = new Basket<String>("1", "2"); System.out.println(b1.equals(b2)); // true System.out.println(b1.equals(b3)); // false System.out.println(b1.equals(b4)); // false } } 看起來不錯,不過來看看下面這個例子:
public class Main { public static void main(String[] args) { Basket<String> b1 = new Basket<String>(); Basket<Integer> b2 = new Basket<Integer>(); System.out.println(b1.equals(b2)); // true } } Basket<Integer>與Basket<String>若是視作不同的型態,則b1與b2
應視為不相等,實際上,由於Java採用型態抹除的方式,結果就是認為在這種情況下,b1與b2是相等的。其實這也可以在以下的例子中看到:
public class Main { public static void main(String[] args) { List<Integer> l1 = new ArrayList<Integer>(); List<String> l2 = new ArrayList<String>(); System.out.println(l1.equals(l2)); // true } } List<Integer>、List<String>是不同的型態,但Java這麼想,l1、l2都是空串列,那它們不就是相等的嗎?這是採取型態抹除的結果。依此類推,Basket<Integer>與Basket<String>是不同的型態沒錯,但你的Basket定義就是比較是不是籃子(Basket<?>),以及實際籃子中放的東西是什麼,現在籃子中沒放東西,所以整個Basket的比較就會是相等的。
以下考慮繼承關係後的equals()、hashCode()定義:
import java.util.*;
class Basket<T> { T[] things; Basket(T... things) { this.things = things; } @Override public boolean equals(Object o) { if(o instanceof Basket<?>) { Basket that = (Basket) o; return that.canEquals(this) && Arrays.deepEquals(this.things, that.things); } return false; } public boolean canEquals(Object other) { return other instanceof Basket<?>; } @Override public int hashCode() { int sum = 1; for(T t : things) { sum = sum * 41 + t.hashCode(); } return 41 * sum + things.hashCode(); } }
|