Java中用静态工厂方法代替构造器的优缺点

Effective Java学习笔记,静态工厂方法的善用。

一般情况下,对于类而言,我们获取一个类的实例时,最常用的方法是提供一个公有的构造器。
但是还有一种方法,类可以提供一个公有的静态工厂方法,它只是个返回类的实例的静态方法而已。但是静态工厂方法却又许多妙用之处。
注意:类的静态工厂方法与设计模式中的工厂方法不同。
Android源码中打量使用了静态工厂方法。例如:
//Watchdog.java
public static Watchdog getInstance() {
        if (sWatchdog == null) {
            sWatchdog = new Watchdog();
        }
        return sWatchdog;
    }
为什么Android源码中会打量使用静态工厂方法呢?静态工厂方法与构造器相比有哪些优势呢?
1)它们有名称。客户端调用的时候见名知意,而且易于阅读。比如getInstance方法,用户调用的时候见名知意,就知道调用该方法可以获得其所在类的一个实例。当一个类有多个相同签名的构造器的时候,就用静态工厂方法代替构造器,避免了用户不知道该调用哪个构造器的问题。
2)不必每次调用它们的时候都创建一个新对象。尤其是在单实例类中使用非常广泛,比如上面列举的Watchdog.java中的getIns()方法,sWatchdog对象实际上只创建了一次,其他需要用Watchdog类的实例的地方,只需要调用getInstance()方法一次,就可以获取该对象,简单方便。
3)可以返回原返回类型的任何子类型对象。这个主要根据传入工厂静态方法的参数来实现,只要是已声名的返回类型的子类型,都是可以的。这为我们选择返回对象的类型提供了灵活性。
例如Android源码中ArraySet.java中的方法toArray,newInstance中的参数array.getClass().getComponentType() 即确定了new出来的对象是什么类型的。 所有的JAVA类都继承自object,那数组也不例外,数组的数据的类型可以是int或者String等,由getComponentType()来决定。这样就体现了上边说的灵活性,返回对象的类型由array决定。
 @Override
    public  T[] toArray(T[] array) {
        if (array.length < mSize) {
            @SuppressWarnings("unchecked") T[] newArray
                = (T[]) Array.newInstance(array.getClass().getComponentType(), mSize);
            array = newArray;
        }
        System.arraycopy(mArray, 0, array, 0, mSize);
        if (array.length > mSize) {
            array[mSize] = null;
        }
        return array;
    }
4)创建参数化类型实例的时候,使得代码更加简洁。
调用参数化构造器的时候,通常要两次提供类型参数:
Map> m = new HashMap>();
使用静态工厂方法后,情况就不一样了:
public static HashMap newInstance( ) {
return new HashMap();
}
于是上边创建m实例的的代码,就变成了下面这样:
Map> m = HashMap.newInstance();
是不是感觉简洁了许多呢?newInstance()只需要写一次,其他地方就可以直接调用了。
任何事物都有其两面性,静态工厂方法当然也有其缺点:
1)类如果不含公有的或者受保护的构造器,就不能被子类化。什么意思呢?就是说如果类没有public或者protected修饰的构造器,那么就不能被另外一个类所继承,也就是所谓的子类化。
2)它们与其他的静态公有方法实际上没有任何区别。
在API文档中,它们没有像构造器那样在API文档中被明确标识出来,因此,对于提供了静态工厂方法而不是构造器的类来说,要想查明如何实例化一个类,是非常困难的。
下面是静态工厂方法的一些惯用名称:
  • valueOf 该方法返回的实例与参数具有相同的值,实际是类型转换方法。
  • of  valueOf的一种替代,EnumSet。
  • getInstance Singleton单例模式,返回唯一的实例。
  • newInstance 返回的实例是一个新的实例,与之前返回的都不相同。
  • getType 与getInstance一样,但是在工厂方法处于不同的类时候使用。
  • newType 与getInstance一样,但是在工厂方法处于不同的类时候使用。
总之,静态工厂方法和公有构造器各有优缺点,我们应该充分认识它们。但是静态工厂方法通常更合适一些,千万不要在创建对象时第一反应就是提供公有的构造器。这破坏了封装性。

你可能感兴趣的:(Java)