通常情况下我们会利用类的构造器对其进行实例化,这似乎毫无疑问。但“静态工厂方法”也需要引起我们的高度注意。
什么是“静态工厂方法”?这不同于设计模式中的工厂方法,我们可以理解它为“在一个类中用一个静态方法来返回这个类的实例”,例如:
public static People getInstance() { return new People(); }
它是一个“方法”,那么它不同于构造器,它可以随意修改方法名,这就带来第一个优点——有名称。有时一个类的构造器往往不止一个,而它们的名称都是相同的,不同的仅仅是参数,如果不同参数带来不同的含义这样对于调用方来说除了注释很难理解它们有什么不同的含义。例如BigInteger(int, int, Random)返回一个素数,但调用者很难理解API设计者所要想表达的意思,如果此时有BigInteger.probablePrime静态工厂方法,则能一目了然的清楚API设计者所要想表达的含义。举一个JDK的例子:Executors类,在这个类中有newFixedThread、newSingleThreadExecutor、newCachedThreadPool等静态方法,因为它们有“名字”,所有就较为清晰的明白API的含义。
《Effective Java》中所提到的静态工厂方法第二个优点在于不用重复创建一个对象,实际上也就是勤加载或者称为饿汉式的单例模式。例如:
public class Instance() { private static Instance instance = new Instance(); private Instance(){} public static Instance getInstance() { return instance; } }
静态工厂方法的第三个优点,可以返回原返回类型的任何子类型的。这句话初看不好理解,举个JDK中的例子:Collections类。
List list = Collections.synchronizedList(new ArrayList())
这个例子就说明了可以返回原返回类型的任何子类型的对象。
关于静态工厂方法的第四个优点,在创建参数化类型实例的时候,它们使代码变得更加简洁,书中举了一个例子:
Map<String, List<String>> m = new HashMap<String, List<String>>(); //这会显得很繁琐
给集合类提供静态工厂方法后:
public static <K, V> HashMap<K, V> newInstance() { return new HashMap<K, V>(); }
但是实际上从JDK7(包括JDK7)之后的集合类可以用以下简洁的代码代替:
Map<String, List<String>> m = new HashMap<>();
静态工厂方法也有两个缺点:一是公有的静态方法所返回的非公有类不能被实例化,也就是说Collections.synchronizedList返回的SynchronizedList不能被实例化;二是查找API比较麻烦,它们不像普通的类有构造器在API中标识出来,而是和其他普通静态方法一样,鉴于此,书中提到了几种惯用名称:
valueOf
of
getInstance
newInstance
getType
newType