Tips
《Effective Java, Third Edition》一書英文版已經出版,這本書的第二版想必不少人都讀過,號稱Java四大名著之一,不過第二版2009年出版,到如今已經將近8年的時間,但隨着Java 6,7,8,甚至9的發佈,Java語言發生了深入的變化。
在這裏第一時間翻譯成中文版。供你們學習分享之用。java
一個類容許客戶端獲取其實例的傳統方式是提供一個公共構造方法。 其實還有另外一種技術應該成爲每一個程序員工具箱的一部分。 一個類能夠提供一個公共靜態工廠方法,它只是一個返回類實例的靜態方法。 下面是一個Boolean
簡單的例子(boolean
基本類型的包裝類)。 此方法將boolean
基本類型轉換爲Boolean
對象引用:程序員
public static Boolean valueOf(boolean b) { return b ? Boolean.TRUE : Boolean.FALSE; }
注意,靜態工廠方法與設計模式中的工廠方法模式不一樣[Gamma95]。本條目中描述的靜態工廠方法在設計模式中沒有直接的等價。數據庫
類能夠爲其客戶端提供靜態工廠方法,而不是公共構造方法。提供靜態工廠方法而不是公共構造方法有優勢也有缺點。設計模式
靜態工廠方法的一個優勢是,不像構造方法,它們是有名字的。 若是構造方法的參數自己並不描述被返回的對象,則具備精心選擇名稱的靜態工廠更易於使用,而且生成的客戶端代碼更易於閱讀。 例如,返回一個可能爲素數的BigInteger
的構造方法BigInteger(int,int,Random)
能夠更好地表示爲名爲BigInteger.probablePrime
的靜態工廠方法。 (這個方法是在Java 1.4中添加的。)數組
一個類只能有一個給定簽名的構造方法。 程序員知道經過提供兩個構造方法來解決這個限制,這兩個構造方法的參數列表只有它們的參數類型的順序不一樣。 這是一個很是糟糕的主意。 這樣的API用戶將永遠不會記得哪一個構造方法是哪一個,最終會錯誤地調用。 閱讀使用這些構造方法的代碼的人只有在參考類文檔的狀況下才知道代碼的做用。緩存
由於他們有名字,因此靜態工廠方法不會受到上面討論中的限制。在類中彷佛須要具備相同簽名的多個構造方法的狀況下,用靜態工廠方法替換構造方法,並仔細選擇名稱來突出它們的差別。框架
靜態工廠方法的第二個優勢是,與構造方法不一樣,它們不須要每次調用時都建立一個新對象。這容許不可變的類(條目17)使用預先構建的實例,或者在構造時緩存實例,並反覆分配它們以免建立沒必要要的重複對象。boolean.valueof(boolean)
方法說明了這種方法:它從不建立對象。這種技術相似於享元模式(Flyweight)[Gamma95]。若是常常請求等價對象,那麼它能夠極大地提升性能,特別是若是在建立它們很是昂貴的狀況下。dom
靜態工廠方法從重複調用返回相同對象的能力容許類保持在任什麼時候候存在的實例的嚴格控制。這樣作的類被稱爲實例控制( instance-controlled)。編寫實例控制類的緣由有不少。實例控制容許一個類來保證它是一個單例(3)項或不可實例化的(條目4)。同時,它容許一個不可變的值類(條目17)保證不存在兩個相同的實例:當且僅當a== b
時a.equals(b)
。這是享元模式的基礎[Gamma95]。Enum
類型(條目34)提供了這個保證。工具
靜態工廠方法的第三個優勢是,與構造方法不一樣,它們能夠返回其返回類型的任何子類型的對象。 這爲你在選擇返回對象的類時提供了很大的靈活性。性能
這種靈活性的一個應用是API能夠返回對象而不須要公開它的類。 以這種方式隱藏實現類會使 API很是緊湊。 這種技術適用於基於接口的框架(條目20),其中接口爲靜態工廠方法提供天然返回類型。
在Java 8以前,接口不能有靜態方法。根據約定,一個名爲Type
的接口的靜態工廠方法被放入一個非實例化的夥伴類(companion class)(條目4)Types
類中。例如,Java集合框架有45個接口的實用工具實現,提供不可修改的集合、同步集合等等。幾乎全部這些實現都是經過靜態工廠方法在一個非實例類(java .util. collections
)中導出的。返回對象的類都是非公開的。
Collections
框架API的規模要比它以前輸出的45個單獨的公共類要小得多,每一個類有個便利類的實現。不只是API的大部分減小了,還包括概念上的權重:程序員必須掌握的概念的數量和難度,才能使用API。程序員知道返回的對象剛好有其接口指定的API,所以不須要爲實現類讀閱讀額外的類文檔。此外,使用這種靜態工廠方法須要客戶端經過接口而不是實現類來引用返回的對象,這一般是良好的實踐(條目64)。
從Java 8開始,接口不能包含靜態方法的限制被取消了,因此一般沒有理由爲接口提供一個不可實例化的伴隨類。 不少公開的靜態成員應該放在這個接口自己。 可是,請注意,將這些靜態方法的大部分實現代碼放在單獨的包私有類中仍然是必要的。 這是由於Java 8要求全部接口的靜態成員都是公共的。 Java 9容許私有靜態方法,但靜態屬性和靜態成員類仍然須要公開。
靜態工廠的第四個優勢是返回對象的類能夠根據輸入參數的不一樣而不一樣。 聲明的返回類型的任何子類都是容許的。 返回對象的類也能夠隨每次發佈而不一樣。
EnumSet
類(條目 36)沒有公共構造方法,只有靜態工廠。 在OpenJDK實現中,它們根據底層枚舉類型的大小返回兩個子類中的一個的實例:若是大多數枚舉類型具備64個或更少的元素,靜態工廠將返回一個RegularEnumSet
實例, 返回一個long
類型;若是枚舉類型具備六十五個或更多元素,則工廠將返回一個JumboEnumSet
實例,返回一個long
類型的數組。
這兩個實現類的存在對於客戶端是不可見的。 若是RegularEnumSet
再也不爲小枚舉類型提供性能優點,則能夠在將來版本中將其淘汰,而不會產生任何不良影響。 一樣,將來的版本可能會添加EnumSet
的第三個或第四個實現,若是它證實有利於性能。 客戶端既不知道也不關心他們從工廠返回的對象的類別; 他們只關心它是EnumSet
的一些子類。
靜態工廠的第5個優勢是,在編寫包含該方法的類時,返回的對象的類不須要存在。這種靈活的靜態工廠方法構成了服務提供者框架的基礎,好比Java數據庫鏈接API(JDBC)。服務提供者框架是提供者實現服務的系統,而且系統使得實現對客戶端可用,從而將客戶端從實現中分離出來。
服務提供者框架中有三個基本組:服務接口,它表示實現;提供者註冊API,提供者用來註冊實現;以及服務訪問API,客戶端使用該API獲取服務的實例。服務訪問API容許客戶端指定選擇實現的標準。在缺乏這樣的標準的狀況下,API返回一個默認實現的實例,或者容許客戶端經過全部可用的實現進行遍歷。服務訪問API是靈活的靜態工廠,它構成了服務提供者框架的基礎。
服務提供者框架的一個可選的第四個組件是一個服務提供者接口,它描述了一個生成服務接口實例的工廠對象。在沒有服務提供者接口的狀況下,必須對實現進行反射實例化(條目65)。在JDBC的狀況下,Connection
扮演服務接口的一部分,DriverManager.registerDriver
提供程序註冊API、DriverManager.getConnection
是服務訪問API,Driver
是服務提供者接口。
服務提供者框架模式有許多變種。 例如,服務訪問API能夠向客戶端返回比提供者提供的更豐富的服務接口。 這是橋接模式[Gamma95]。 依賴注入框架(條目5)能夠被看做是強大的服務提供者。 從Java 6開始,平臺包含一個通用的服務提供者框架java.util.ServiceLoader
,因此你不須要,通常也不該該本身編寫(條目59)。 JDBC不使用ServiceLoader
,由於前者早於後者。
只提供靜態工廠方法的主要限制是,沒有公共或受保護構造方法的類不能被子類化。例如,在Collections
框架中不可能將任何方便實現類子類化。能夠說,這多是塞翁失馬,由於它鼓勵程序員使用組合而不是繼承(條目18),而且是不可變類型(條目17)。
靜態工廠方法的第二個缺點是,程序員很難找到它們。它們不像構造方法那樣在API文檔中突出,所以很難找出如何實例化一個提供靜態工廠方法而不是構造方法的類。Javadoc工具可能有一天會引發對靜態工廠方法的注意。與此同時,能夠經過將注意力吸引到類或接口文檔中的靜態工廠以及遵照通用的命名約定來減小這個問題。下面是一些靜態工廠方法的經常使用名稱。如下清單並不是完整:
Date d = Date.from(instant)
;Set<Rank> faceCards = EnumSet.of(JACK, QUEEN, KING)
;BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE)
;StackWalker luke = StackWalker.getInstance(options)
;Object newArray = Array.newInstance(classObject, arrayLen)
;FileStore fs = Files.getFileStore(path)
;BufferedReader br = Files.newBufferedReader(path)
;List<Complaint> litany = Collections.list(legacyLitany)
;總之,靜態工廠方法和公共構造方法都有它們的用途,而且瞭解它們的相對優勢是值得的。一般,靜態工廠更可取,所以避免在沒有考慮靜態工廠的狀況下提供公共構造方法。