Tips
《Effective Java, Third Edition》一書英文版已經出版,這本書的第二版想必不少人都讀過,號稱Java四大名著之一,不過第二版2009年出版,到如今已經將近8年的時間,但隨着Java 6,7,8,甚至9的發佈,Java語言發生了深入的變化。
在這裏第一時間翻譯成中文版。供你們學習分享之用。java
Java類庫包含幾個註解類型。對於典型的程序員來講,最重要的是@Override
。此註解只能在方法聲明上使用,它代表帶此註解的方法聲明重寫了父類的聲明。若是始終使用這個註解,它將避免產生大量的惡意bug。考慮這個程序,在這個程序中,類Bigram
表示雙字母組合,或者是有序的一對字母:程序員
// Can you spot the bug? 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 b) { return b.first == first && b.second == second; } public int hashCode() { return 31 * first + second; } public static void main(String[] args) { Set<Bigram> s = new HashSet<>(); for (int i = 0; i < 10; i++) for (char ch = 'a'; ch <= 'z'; ch++) s.add(new Bigram(ch, ch)); System.out.println(s.size()); } }
主程序重複添加二十六個雙字母組合到集合中,每一個雙字母組合由兩個相同的小寫字母組成。 而後它會打印集合的大小。 你可能但願程序打印26,由於集合不能包含重複項。 若是你嘗試運行程序,你會發現它打印的不是26,而是260。它有什麼問題?ide
顯然,Bigram
類的做者打算重寫equals
方法(條目 10),甚至記得重寫hashCode
(條目 11)。 不幸的是,咱們倒黴的程序員沒有重寫equals
,而是重載它(條目 52)。 要重寫Object.equals,必須定義一個equals方法,其參數的類型爲Object,但Bigram
的equals方法的參數不是Object類型的,所以Bigram
繼承Object的equals方法,這個equals方法測試對象的引用是不是同一個,就像==運算符同樣。 每一個祖母組合的10個副本中的每個都與其餘9個副本不一樣,因此它們被Object.equals視爲不相等,這就解釋了程序打印260的緣由。學習
幸運的是,編譯器能夠幫助你找到這個錯誤,但只有當你經過告訴它你打算重寫Object.equals來幫助你。 要作到這一點,用@Override
註解Bigram.equals方法,以下所示:測試
@Override public boolean equals(Bigram b) { return b.first == first && b.second == second; }
若是插入此註解並嘗試從新編譯該程序,編譯器將生成以下錯誤消息:this
Bigram.java:10: method does not override or implement a method from a supertype @Override public boolean equals(Bigram b) { ^
你會馬上意識到你作錯了什麼,在額頭上狠狠地打了一下,用一個正確的(條目 10)來替換出錯的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
註解。 這條規則有一個小例外。 若是正在編寫一個沒有標記爲抽象的類,而且確信它重寫了其父類中的抽象方法,則無需將Override
註解放在該方法上。 在沒有聲明爲抽象的類中,若是沒法重寫抽象父類方法,編譯器將發出錯誤消息。 可是,你可能但願關注類中全部重寫父類方法的方法,在這種狀況下,也應該隨時註解這些方法。 大多數IDE能夠設置爲在選擇重寫方法時自動插入Override
註解。code
大多數IDE提供了是種使用Override
註解的另外一個理由。 若是啓用適當的檢查功能,若是有一個方法沒有Override
註解可是重寫父類方法,則IDE將生成一個警告。 若是始終使用Override
註解,這些警告將提醒你無心識的重寫。 它們補充了編譯器的錯誤消息,這些消息會提醒你無心識重寫失敗。 IDE和編譯器,能夠確保你在任何你想要的地方和其餘地方重寫方法,萬無一失。對象
Override
註解可用於重寫來自接口和類的方法聲明。 隨着default默認方法的出現,在接口方法的具體實現上使用Override
以確保簽名是正確的是一個好習慣。 若是知道某個接口沒有默認方法,能夠選擇忽略接口方法的具體實現上的Override
註解以減小混亂。blog
然而,在一個抽象類或接口中,值得標記的是你認爲重寫父類或父接口方法的全部方法,不管是具體的仍是抽象的。 例如,Set接口不會向Collection接口添加新方法,所以它應該在其全部方法聲明中包含Override
註解以確保它不會意外地向Collection接口添加任何新方法。
總之,若是在每一個方法聲明中使用Override
註解,而且認爲要重寫父類聲明,那麼編譯器能夠保護免受不少錯誤的影響,但有一個例外。 在具體的類中,不須要註解標記你確信能夠重寫抽象方法聲明的方法(儘管這樣作也沒有壞處)。