Effective Item - 考慮用靜態工廠方法代替構造器我們有兩種常見的方法獲得一個類的實例:
相對公有的構造器,靜態工廠方法有以下幾大優勢。
優勢1.靜態工廠方法的名稱,因此比構造器更准確地描述返回的實例。
比如BigInteger.probablePrime方法:
public static BigInteger probablePrime(int bitLength, Random rnd) {
if (bitLength < 2)
throw new ArithmeticException("bitLength < 2");
// The cutoff of 95 was chosen empirically for best performance
return (bitLength < SMALL_PRIME_THRESHOLD ?
smallPrime(bitLength, DEFAULT_PRIME_CERTAINTY, rnd) :
largePrime(bitLength, DEFAULT_PRIME_CERTAINTY, rnd));
}
順便也貼出其調用的largePrime方法:
private static BigInteger largePrime(int bitLength, int certainty, Random rnd) {
BigInteger p;
p = new BigInteger(bitLength, rnd).setBit(bitLength-1);
p.mag[p.mag.length-1] &= 0xfffffffe;
// Use a sieve length likely to contain the next prime number
int searchLen = (bitLength / 20) * 64;
BitSieve searchSieve = new BitSieve(p, searchLen);
BigInteger candidate = searchSieve.retrieve(p, certainty, rnd);
while ((candidate == null) || (candidate.bitLength() != bitLength)) {
p = p.add(BigInteger.valueOf(2*searchLen));
if (p.bitLength() != bitLength)
p = new BigInteger(bitLength, rnd).setBit(bitLength-1);
p.mag[p.mag.length-1] &= 0xfffffffe;
searchSieve = new BitSieve(p, searchLen);
candidate = searchSieve.retrieve(p, certainty, rnd);
}
return candidate;
}
雖然smallPrime和largePrime最後都是通過公有構造器返回實例。
但是如果僅僅用構造器重載表達這個實例的特征,這很難讓人記住什麼時候應該調用什麼構造器。
而提供一個名稱去描述實例更為直觀。
優勢2.靜態工廠方法不必每次都創建一個新的對象,我們可以對實例進行控制。
這樣我們就能將創建好的實例緩存起來重復利用,尤其是在創建對象的代價較高的情況下。
比如Boolean.valueOf:
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);
}
優勢3.靜態工廠方法可以返回原返回類型的子類型對象。
這一點能體現靜態工廠方法的靈活性,
以EnumSet為例:
/**
* Creates an empty enum set with the specified element type.
*
* @param elementType the class object of the element type for this enum
* set
* @throws NullPointerException if <tt>elementType</tt> is null
*/
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);
}
而RegularEnumSet和JumboEnumSet為EnumSet的子類,並且都沒有提供公有構造器。
優勢4.靜態工廠方法創建參數化(泛型)實例的時候更加簡潔。
舉個例子:
public static <K, V> HashMap<K, V> newInstance() {
return new HashMap<K, V>();
}
這樣一來創建實例時就可以:
Map<String,List<Integer>> n = newInstance();
而不是
Map<String,List<Integer>> m = new HashMap<String,List<Integer>>();
從Java7開始這一點變得沒有意義,事實上Josh Bloch也在書上提到了這點——Java以後會在構造器和方法調用中執行這種類型推導。
說說靜態工廠方法的缺點。