考慮下面的程序,類Bigram 表示一個雙字母組,考慮打印語句的輸出結果
package thirtysix;
import java.util.HashSet;
import java.util.Set;
/**
* Bigram
* @author ZHAIYANMING
* @version 1.0
*
*/
public class Bigram {
private final char first;
private final char second;
public Bigram(char first, char second) {
this.first = first;
this.second = second;
}
public boolean equals(Bigram bigram) {
return bigram.first == first && bigram.second == second;
}
public int hashCode() {
return 31 * first + second;
}
public static void main(String[] args) {
Set<Bigram> bigrams = new HashSet<Bigram>();
for (int i = 0; i < 10; i++) {
for (char ch = 'a'; ch <= 'z'; ch++) {
bigrams.add(new Bigram(ch, ch));
}
}
System.out.println(bigrams.size());
}
}
為什么結果是這樣?bigrams采用的集合類型是HashSet,為什么沒有去掉重復的元素呢?
/**
* Adds the specified element to this set if it is not already present.
* More formally, adds the specified element <tt>e</tt> to this set if
* this set contains no element <tt>e2</tt> such that
* <tt>(e==null ? e2==null : e.equals(e2))</tt>.
* If this set already contains the element, the call leaves the set
* unchanged and returns <tt>false</tt>.
*
* @param e element to be added to this set
* @return <tt>true</tt> if this set did not already contain the specified
* element
*/
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
HashSet的add方法的底層說明:3~5行說明了add元素e的條件:set中不存在元素e2( e==null;e2==null;e.equals(e2) )。在元素都不為null的情況下,說明這行代碼e.equals(e2) 與我們想要的結果不一樣.
public boolean equals(Object obj) {
return (this == obj);
}
看一下Object類中的equals方法。發現代碼中原本想要覆蓋equals方法(發生覆蓋的條件:“三同一不低”,子類和父類的方法名稱,參數列表,返回類型必須完全相同,而且子類方法的訪問修飾符的權限不能比父類低。),可是卻重載了equals方法(發生重載的條件:方法名相同,參數列表(類型、個數、順序)不同)。在進行這行代碼( e.equals(e2) )時,就調用了Object類的equals方法,而Object類的equals方法是比較對象同一性的,即( e==e2 )。例子中:這行代碼:bigrams.add(new Bigram(ch, ch)); 每次添加都會new Bigram(ch,ch),每一個Object都不一樣,自然循環多少次,就add多少Object了。
隨著java 1.5發行版本中增加注解,類庫中也增加了幾種注解類型。其中Override只能用在方法聲明中,它表示被注解的方法聲明覆蓋了超類型中的一個聲明。堅持使用這個注解,可以防止一大類非法錯誤。
@Override public boolean equals(Bigram bigram) {
return bigram.first == first && bigram.second == second;
}
如果插入這個注解,重新編譯程序,編譯器會產生一條錯誤:
The method equals(Bigram) of type Bigram must override or implement a supertype method
針對提示的錯誤咱們就可以修改相應的代碼,這個equals方法測試對象的同一性,就像==:
@Override public boolean equals(Object o) {
if(!(o instanceof Bigram)) {
return false;
}
Bigram b = (Bigram) o;
return b.first == first && b.second == second;
}
總結:如果在你想要的每個方法聲明中使用 Override 注解來覆蓋超類聲明 , 編譯器就可以替你防止大量錯誤 。在具體的類中 , 不必標注你確信覆蓋了抽象方法聲明的方法(標注也可以)。