本謎題試圖從前一個(gè)謎題中吸取教訓(xùn)。下面的程序還是由一個(gè)Name類和一個(gè)main方法構(gòu)成,這個(gè)main方法還是將一個(gè)名字放置到一個(gè)散列集合中,然后檢查該集合是否包含了這個(gè)名字。然而,這一次Name類已經(jīng)覆寫了hashCode方法。那么下面的程序?qū)⒋蛴〕鍪裁茨兀?BR> import java.util.*;
public class Name {
private String first, last;
public Name(String first, String last) {
this.first = first; this.last = last;
}
public boolean equals(Name n) {
return n.first.equals(first) && n.last.equals(last);
}
public int hashCode() {
return 31 * first.hashCode() + last.hashCode();
}
public static void main(String[ ] args) {
Set s = new HashSet();
s.add(new Name("Donald", "Duck"));
System.out.println(
s.contains(new Name("Donald", "Duck")));
}
}
與謎題57一樣,該程序的main方法創(chuàng)建了兩個(gè)Name實(shí)例,它們表示的是相同的名字。這一次使用的名字是Donald Duck而不是Mickey Mouse,但是它們不應(yīng)該有很大的區(qū)別。main方法同樣還是將第一個(gè)實(shí)例置于一個(gè)散列集合中,然后檢查該集合中是否包含了第二個(gè)實(shí)例。這一次hashCode方法明顯是正確的,因此看起來(lái)該程序應(yīng)該打印true。但是,表象再次欺騙了我們:它總是打印出false。這一次又是哪里出錯(cuò)了呢?
這個(gè)程序的缺陷與謎題57中的缺陷很相似,在謎題57中,Name覆寫了equals方法,但是沒(méi)有覆寫hashCode方法;而在本謎題中,Name覆寫了hashCode方法,但是沒(méi)有覆寫equals方法。這并不是說(shuō)Name沒(méi)有聲明一個(gè)equals方法,它確實(shí)聲明了,但是那是個(gè)錯(cuò)誤的聲明。Name類聲明了一個(gè)參數(shù)類型是Name而不是Object的equals方法。這個(gè)類的作者可能想要覆寫equals方法,但是卻錯(cuò)誤地重載了它[JLS 8.4.8.1, 8.4.9]。
HashSet類是使用equals(Object)方法來(lái)測(cè)試元素的相等性的;Name類中聲明一個(gè)equals(Name)方法對(duì)HashSet不造成任何影響。那么Name是從哪里得到了它的equals(Object)方法的呢?它是從Object哪里繼承而來(lái)的。這個(gè)方法只有在它的參數(shù)與在其上調(diào)用該方法的對(duì)象完全相同時(shí)才返回true。我們的程序中的main方法將一個(gè)Name實(shí)例插入到了散列集合中,并且測(cè)試另一個(gè)實(shí)例是否存在于該散列集合中,由此可知該測(cè)試一定是返回false的。對(duì)我們而言,兩個(gè)實(shí)例可以代表那令人驚奇的水禽唐老鴨,但是對(duì)散列映射表而言,它們只是兩個(gè)不相等的對(duì)象。
public class Name {
private String first, last;
public Name(String first, String last) {
this.first = first; this.last = last;
}
public boolean equals(Name n) {
return n.first.equals(first) && n.last.equals(last);
}
public int hashCode() {
return 31 * first.hashCode() + last.hashCode();
}
public static void main(String[ ] args) {
Set s = new HashSet();
s.add(new Name("Donald", "Duck"));
System.out.println(
s.contains(new Name("Donald", "Duck")));
}
}
與謎題57一樣,該程序的main方法創(chuàng)建了兩個(gè)Name實(shí)例,它們表示的是相同的名字。這一次使用的名字是Donald Duck而不是Mickey Mouse,但是它們不應(yīng)該有很大的區(qū)別。main方法同樣還是將第一個(gè)實(shí)例置于一個(gè)散列集合中,然后檢查該集合中是否包含了第二個(gè)實(shí)例。這一次hashCode方法明顯是正確的,因此看起來(lái)該程序應(yīng)該打印true。但是,表象再次欺騙了我們:它總是打印出false。這一次又是哪里出錯(cuò)了呢?
這個(gè)程序的缺陷與謎題57中的缺陷很相似,在謎題57中,Name覆寫了equals方法,但是沒(méi)有覆寫hashCode方法;而在本謎題中,Name覆寫了hashCode方法,但是沒(méi)有覆寫equals方法。這并不是說(shuō)Name沒(méi)有聲明一個(gè)equals方法,它確實(shí)聲明了,但是那是個(gè)錯(cuò)誤的聲明。Name類聲明了一個(gè)參數(shù)類型是Name而不是Object的equals方法。這個(gè)類的作者可能想要覆寫equals方法,但是卻錯(cuò)誤地重載了它[JLS 8.4.8.1, 8.4.9]。
HashSet類是使用equals(Object)方法來(lái)測(cè)試元素的相等性的;Name類中聲明一個(gè)equals(Name)方法對(duì)HashSet不造成任何影響。那么Name是從哪里得到了它的equals(Object)方法的呢?它是從Object哪里繼承而來(lái)的。這個(gè)方法只有在它的參數(shù)與在其上調(diào)用該方法的對(duì)象完全相同時(shí)才返回true。我們的程序中的main方法將一個(gè)Name實(shí)例插入到了散列集合中,并且測(cè)試另一個(gè)實(shí)例是否存在于該散列集合中,由此可知該測(cè)試一定是返回false的。對(duì)我們而言,兩個(gè)實(shí)例可以代表那令人驚奇的水禽唐老鴨,但是對(duì)散列映射表而言,它們只是兩個(gè)不相等的對(duì)象。