這是Effective java第2章提出的第一條建議:
考慮用靜態(tài)工廠方法代替構(gòu)造器
此處的靜態(tài)工廠方法并不是設(shè)計(jì)模式,主要指static修飾的靜態(tài)方法,關(guān)于static的說(shuō)明可以參考之前的博文《java中final與static的使用場(chǎng)景總結(jié)》。
可以參考書中的例子(摘自JDK1.7 java.lang.Boolean)
public final class Boolean implements java.io.Serializable, Comparable<Boolean> { public static final Boolean TRUE = new Boolean(true); public static final Boolean FALSE = new Boolean(false); public static Boolean valueOf(boolean b) { return (b ? TRUE : FALSE); }}如果需要獲取一個(gè)Boolean對(duì)象,常規(guī)的方法是new Boolean(true),但是也可以如上圖所示Boolean.valueOf(true),這便是靜態(tài)工廠方法。
靜態(tài)工廠方法與構(gòu)造器不同的第一大優(yōu)勢(shì)在于,它們有名稱。
使用構(gòu)造函數(shù)構(gòu)造對(duì)象時(shí),我們需要通過(guò)文檔仔細(xì)比對(duì)傳遞什么樣的參數(shù)能夠構(gòu)造什么樣的對(duì)象。但是靜態(tài)工廠方法可以使用不同的方法名字使得其構(gòu)造的對(duì)象更加明晰。我們完全可以通過(guò)方法名明白構(gòu)造了什么樣的對(duì)象。
例如下面的例子(摘自JDK1.7 java.math.BigInteger)
public class BigInteger extends Number implements Comparable<BigInteger> { /** * Returns a positive BigInteger that is PRobably prime, with the * specified bitLength. The probability that a BigInteger returned * by this method is composite does not exceed 2<sup>-100</sup>. */ public static BigInteger probablePrime(int bitLength, Random rnd) { // XXX }}靜態(tài)工廠方法與構(gòu)造器不同的第二大優(yōu)勢(shì)在于,不必在每次調(diào)用它們的時(shí)候都創(chuàng)建一個(gè)新對(duì)象。
我們調(diào)用靜態(tài)工廠方法返回的可能是緩存的一個(gè)對(duì)象,而不是新對(duì)象。可以進(jìn)行重復(fù)利用,從而避免創(chuàng)建不必要的重復(fù)對(duì)象。
如果程序經(jīng)常請(qǐng)求創(chuàng)建相同的對(duì)象,并且創(chuàng)建的代價(jià)很高,則靜態(tài)工廠方法可以極大地提升性能。
前面提到的Boolean.valueOf(boolean)便說(shuō)明了這項(xiàng)技術(shù)。
靜態(tài)工廠方法與構(gòu)造器不同的第三大優(yōu)勢(shì)在于,他們可以返回原返回類型的任何子類型的對(duì)象。
我們?cè)谶x擇返回對(duì)象的類時(shí)有了更大的靈活性。參見(jiàn)java.util.EnumSet,其本身被abstract修飾,無(wú)法直接調(diào)用其構(gòu)造函數(shù)。但可以調(diào)用其靜態(tài)方法noneOf來(lái)創(chuàng)建對(duì)象,并且根據(jù)參數(shù)返回合適的對(duì)象。
public abstract class EnumSet<E extends Enum<E>> extends AbstractSet<E> implements Cloneable, java.io.Serializable { EnumSet(Class<E>elementType, Enum[] universe) { } //RegularEnumSet與JumboEnumSet均為EnumSet的子類 public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) { if (universe.length <= 64) return new RegularEnumSet<>(elementType, universe); else return new JumboEnumSet<>(elementType, universe); }}第四大優(yōu)勢(shì)在于,創(chuàng)建參數(shù)化類型實(shí)例的時(shí)候,可以使代碼變得更加簡(jiǎn)潔。
例如對(duì)于HashMap的實(shí)例化:
//常規(guī)實(shí)例化方式Map<String, List<String>> m = new HashMap<String, List<String>>();public static <K, V> HashMap<K, V> newInstance() { return new HashMap<K, V>();}//使用靜態(tài)工廠方法實(shí)例化,簡(jiǎn)化繁瑣的聲明Map<String, List<String>> m = HashMap.newInstance();類如果不含公有的或受保護(hù)的構(gòu)造器,就不能被實(shí)例化。
如果我們?cè)陬愔袑?gòu)造函數(shù)設(shè)為private,只提供靜態(tài)工廠方法來(lái)構(gòu)建對(duì)象,那么我們將不能通過(guò)繼承擴(kuò)展該類。
但是這也會(huì)鼓勵(lì)我們使用復(fù)合而不是繼承來(lái)擴(kuò)展類。
它們與其他的靜態(tài)方法實(shí)際上沒(méi)有任何區(qū)別。
在API文檔中,構(gòu)建對(duì)象的靜態(tài)工廠方法并沒(méi)有像構(gòu)造器那樣明確標(biāo)識(shí)出來(lái),不能和其他靜態(tài)方法很方便地區(qū)分開(kāi)來(lái)。
如果類中只提供靜態(tài)工廠方法而不是構(gòu)造器,要想查明如何實(shí)例化一個(gè)類將會(huì)變得困難。
我們可以通過(guò)遵循靜態(tài)工廠方法的命名規(guī)范來(lái)彌補(bǔ)這一劣勢(shì):
Boolean.valueOf(boolean)總而言之,靜態(tài)工廠方法和公有構(gòu)造器具有各自的用處,但靜態(tài)工廠方法通常更加合適,所以我們應(yīng)該優(yōu)先考慮靜態(tài)工廠方法。
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注