長角的東西怎麼比


物件相等性 討論過如何重新定義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的泛型所採用的是型態抹除,也就是說,程式中泛型語法的 型態指定,僅提供編譯器使用,執行時期無法獲型態資訊,因而instanceof在執行時期比對時,僅能針對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();
}
}