Effective Java 第三版讀書筆記——條款1.考慮使用靜態工廠方法替代構造器
獲取一個類的實例的傳統方法是使用公開的構造器,除此之外,一個類還可以提供公開的靜態工廠方法(static factory method)來返回它的實例。例如 Boolean
類中的 valueOf
方法,這個方法將基本類型 boolean
轉換為一個 Boolean
對象的引用:
public static Boolean valueOf(boolean b) {
return b ? Boolean.TRUE : Boolean.FALSE;
}
註:靜態工廠方法與設計模式中的工廠方法不同。
靜態工廠方法的優點:
與構造器不同,靜態工廠方法擁有自己的名字。考慮一個場景:
BigInterger
BigInterger(int, int, Random)
可能返回一個素數,然而傳統的構造器不能很直觀地表達出來,我們可以用一個靜態工廠方法BigInteger.probablePrime
使表達變得更清晰。此外,如果一個類需要幾個參數類型相同的構造器,它只能打亂參數的順序來進行區別,這使得類的構造變得十分困難。此時可以考慮使用靜態工廠方法,這些方法擁有相同的參數類型,但你可以賦予它們精心挑選的名字,使它們的區別變得明顯。
當靜態工廠方法被調用時,它們不需要每一次都創建一個新的對象。如開篇的
Boolean.valueOf(boolean)
所示,這個方法從來不創建對象,它只是返回Boolean
public static final Boolean TRUE = new Boolean(true); public static final Boolean FALSE = new Boolean(false);
這有效地提升了程序的性能。這一技巧與設計模式中的享元模式(Flyweight)十分相似。
靜態工廠方法可以返回其返回類型的任何子類的對象。一個可能的應用場景:API 可以返回對象,而不需要將它們的類公之於眾,以這種方式隱藏實現類將使這個 API 變得十分緊湊。這個技術適合於基於接口的框架(interface-based frameworks)。
隨著輸入參數的不同,同一個靜態工廠方法可以返回不同的子類。例如
EnumSet
public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) { Enum<?>[] universe = getUniverse(elementType); if (universe == null) throw new ClassCastException(elementType + " not an enum"); if (universe.length <= 64) return new RegularEnumSet<>(elementType, universe); else return new JumboEnumSet<>(elementType, universe); }
當底層枚舉類型的元素不超過64個時,返回
RegularEnumSet
,超過64個則返回JumboEnumSet
。在編寫包含靜態方法的類時,返回對象的類不需要存在。這種靈活的靜態工廠方法構成了服務提供者框架(service provider frameworks)的基礎,比如Java的數據庫連接 API(JDBC)。
靜態工廠方法的缺點
- 只提供靜態工廠方法、不提供
public
或protected
構造器的類不能被繼承(子類化)。這也可能因禍得福:它鼓勵程序員使用組合而不是繼承。 - 靜態工廠方法很難被程序員找到。在API文檔中,靜態工廠方法不能像構造器那樣突出,這使得程序員難以找到它們來實例化一個類。
一些靜態工廠方法的常用名稱
from
:一個類型轉換方法,接受一個參數並返回這個類型的相應實例。Date d = Date.from(instant);
of
:一個聚合方法,接受多個參數、將它們合並在一起並返回這個類型的相應實例。Set<Rank> faceCards = EnumSet.of(JACK, QUEEN, KING);
valueOf
:from
和to
的更為詳細的替代方式。BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE);
instance
或getInstance
:返回一個實例,該實例由其參數(如果有的話)描述,但不能說具有相同的值。StackWalker luke = StackWalker.getInstance(options);
create
或newInstance
:與instance
或getInstance
類似,除了該方法保證每個調用返回一個新的實例。Object newArray = Array.newInstance(classObject, arrayLen);
getType
:與getInstance
類似,當工廠方法在不同的類中時使用。Type 是工廠方法返回對象的類型。FileStore fs = Files.getFileStore(path);
newType
:與newInstance
類似,當工廠方法在不同的類中時使用。Type 是工廠方法返回對象的類型。BufferedReader br = Files.newBufferedReader(path);
type
:getType
和newType
的簡潔版。List<Complaint> litany = Collections.list(legacyLitany);
Effective Java 第三版讀書筆記——條款1.考慮使用靜態工廠方法替代構造器