考虑用静态工厂方法代替构造器我们有两种常见的方法获得一个类的实例.
静态工厂方法的优点:
优势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. 静态工厂方法不必每次都创建一个新的对象,我们可以对实例进行控制。这样我们就能将创建好的实例缓存起来重复利用,尤其是在创建对象的代价较高的情况下。 除此之外,可以用==代替equals()方法,达到性能的提升。
对比:
调用构造器时每次都创建新对象。
比如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. 静态工厂方法可以返回原返回类型的子类型对象。这一点能体现静态工厂方法的灵活性.
对比:
构造器只能返回当前类的实例,无法返回子类的实例。
1) Interface-based framework. Interface 不能有 static methods (这是 Java 的规范,理解为 interface 只规定 contract,而 static menthod 也算一种 implementation)。那么按照惯例,一个叫做 "Type" 的interface 的 static methods 就放在叫做 "Types" 的这个类中,并且 Types 不可实例化。举两个例子:
public class Arrays {
// Suppresses default constructor, ensuring non-instantiability.
private Arrays() {
}
public static void sort(long[] a, int fromIndex, int toIndex) {
rangeCheck(a.length, fromIndex, toIndex);
sort1(a, fromIndex, toIndex-fromIndex);
}
<pre style="word-wrap: break-word; white-space: pre-wrap;"> public static void sort(int[] a) {
sort1(a, 0, a.length);
}
// ...}
2) 以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>>();