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);