首先,什么是静态工厂方法。一般而言,如果一个类需要调用另一个类的变量和方法,那么该类需要先获得另一个类的实例,最常用的的方法就是被调用类提供一个公有的构造方法。但是,还要另外一种方法,就是被调用类提供一个可以返回该类实例的静态方法,该静态方法称为静态工厂方法
。例如下面的例子(引自《Effective Java》),将boolean基本类型值转换成了一个Boolean对象引用:
public static Boolean valueOf(boolean b) {
return b ? Boolean.TRUE : Boolean.FALSE;
}
优点
1. 和构造方法不一样,静态工厂方法可以有自己明确的名字。比如newInstance
、getInstance
、valueOf
,可以更明确知道这些方法名字的意思。而构造方法只能通过new类名来创建,比如Person person = new Person()
。
2. 不必没次调用时都创建一个新对象。使用静态工厂方法对外提供单例,其实就是提前准备好实例,这样可以重复利用,减少重复创建实例,不过这也需要注意线程安全问题。
3. 可以返回原返回类型的任何子类的对象。这个可以这么理解,例如EnumSet
类,EnumSet
是抽象类是不能被实例化的,但是可以通过里面的noneOf
静态工厂方法返回它子类的对象,RegularEnumSet
和JumboEnumSet
都是EnumSet
的子类,代码如下:
public abstract class EnumSet<E extends Enum<E>> extends AbstractSet<E>
implements Cloneable, java.io.Serializable {
EnumSet(Class<E>elementType, Enum[] universe) {}
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);
}
}
4.在创建参数化类型实例的时候,它们使代码变得更加简洁。这条可以这么理解,比如在创建一个Map
类型对象时,我们一般可以这么创建:
Map<String, List<String>> m = new HashMap<String, List<String>>();
但是在JDK7以上的版本,泛型参数是可以被推导出来的,所以可以有更简洁的写法:
Map<String, List<String>> m = new HashMap<>();
或者以后JDK可以提供这样的写法,但目前还不能:
Map<String, List<String>> m = HashMap.newInstance();
虽然标准的集合实现如HashMap并没有工厂方法,但我们可以把这样方法封装在自己的工具类中。
缺点
1. 类如果不含有公有的或者受保护的构造方法,就不能被继承。如果使用静态工厂方法获得实例,而不提供公有的构造方法,把构造方法写为私有private
,那么该类就不能被继承扩展,这就是使用复合而不是继承来扩展类了。
2.与其他的静态方法实际上没有任何区别。静态工厂方法其实和其他普通的静态方法是一样的,如果没有加以说明,两者不能很好被区分开来,会造成使用者不知道怎么获取该类的实例,而且不能清晰查明该类是如何被实例化的。
简而言之,静态工厂方法和公有构造方法都各有用处,我们需要理解它们各自的长处。静态工厂通常更加合适,因此切忌第一反应就是提供公有的构造器,而不先考虑静态工厂。
参考
《Effective Java 中文版 第2版》