Tips
書中的源代碼地址:https://github.com/jbloch/effective-java-3e-source-code
注意,書中的有些代碼裏方法是基於Java 9 API中的,因此JDK 最好下載 JDK 9以上的版本。java
條目 51中指出,應該使用接口而不是類做爲參數類型。更一般地說,應該更喜歡使用接口而不是類來引用對象。若是存在適當的接口類型,那麼應該使用接口類型聲明參數、返回值、變量和屬性。真正須要引用對象的類的唯一時機是使用構造方法建立它的時候。爲了具體說明這一點,考慮LinkedHashSet的狀況,它是Set接口的一個實現。養成這樣的習慣:git
// Good - uses interface as type Set<Son> sonSet = new LinkedHashSet<>();
不要是下面這個樣子:github
// Bad - uses class as type! LinkedHashSet<Son> sonSet = new LinkedHashSet<>();
若是養成了使用接口做爲類型的習慣,那麼你的程序將更加靈活。若是決定要切換實現,只需在構造方法中更改類名(或使用不一樣的靜態工廠)。例如,第一個聲明能夠改成:框架
Set<Son> sonSet = new HashSet<>();
全部的代碼都會繼續工做。周圍的代碼不知道舊的實現類型,因此它不會注意到這一變化。性能
有一點須要注意:若是原始實現提供了接口的常規約定不須要的某些特殊功能,而且代碼依賴於該功能,那麼新實現提供相同的功能相當重要。 例如,若是圍繞第一個聲明的代碼依賴於LinkedHashSet的排序策略,那麼在聲明中用HashSet替換LinkedHashSet是不正確的,由於HashSet不保證迭代順序。code
那麼,爲何要更改實現類型呢?由於第二個實現比原來的實現提供了更好的性能,或者由於它提供了原來的實現所缺少的理想功能。例如,假設一個屬性包含一個HashMap實例。將其更改成EnumMap將提供更好的性能和與鍵的天然順序一致的迭代順序,可是隻能在鍵類型爲枚舉類型的狀況下使用EnumMap。將HashMap更改成LinkedHashMap將提供可預測的迭代順序,性能與HashMap至關,而不須要對鍵類型做出任何特殊要求。對象
你可能認爲使用變量的實現類型聲明變量是能夠的,由於能夠同時更改聲明類型和實現類型,可是不能保證這種更改可否正常編譯。若是客戶端代碼對原始實現類型使用了替換時不存在的方法,或者客戶端代碼將實例傳遞給須要原始實現類型的方法,那麼在進行此更改以後,代碼將再也不編譯。使用接口類型聲明變量能夠保持誠實可靠。blog
若是不存在適當的接口,則經過類而不是接口引用對象是徹底合適的。 例如,考慮值類,例如String和BigInteger。 值類不多用多個實現類來編寫。 它們一般是final的,不多有相應的接口。 將這樣的值類用做參數,變量,屬性或返回類型是徹底合適的。排序
沒有適當接口類型的第二種狀況是屬於框架的對象,其基本類型是類而不是接口。 若是一個對象屬於這樣一個基於類的框架,最好用相關的基類來引用它,它一般是抽象類,而不是它的實現類。 許多java.io包下的類(如OutputStream)都屬於此類。接口
沒有適當接口類型的最後一種狀況是實現接口的類,但也提供了在接口中找不到的額外方法——例如,PriorityQueue具備Queue接口上不存在的comparator方法。 只有當程序依賴於額外的方法時,才應該使用這樣的類來引用它的實例,這種狀況應該是很是少見的。
這三種狀況並非面面俱到的,而僅僅是爲了傳達經過合適的類引用對象的狀況。在實踐中,給定對象是否具備適當的接口應該是顯而易見的。若是是這樣,使用接口引用對象,程序將更加靈活和流行。若是沒有合適的接口,就使用類層次結構中提供所需功能的最小的具體類。