本条目中所指的静态工厂方法并不直接对应于设计模式( Design Pattern )中的工厂方法 。
下面是一个来自 Boolean (基本类型 boolean 的装箱类)的简单示例 。 这个方法将 boolean 基本类型值转换成了一个 Boolean 对象引用:
public static Boolean valueOf(boolean b) {return b? Boolean.TRUE : Boolean.FALSE;}
对于类而言,为了让客户端获取它自身的一个实例,最传统的方法就是提供一个公有的构造器 。
还有一种方法,类可以提供一个公有的静态 工厂 方法( static factory method ),它只是一个返回类的实例的静态方法 。
如果不通过公有的构造器,或者说除了公有的构造器之外,类还可以给它的客户端提供静态工厂方法 。 提供静态工厂方法而不是公有的构造器,这样做既有优势,也有劣势 。
静态工厂方法与构造器不同的优势
1、静态工厂方法有名称。
如果构造器的参数本身没有确切地描述正被返回的对象,那么具有适当名称的静态工厂会更容易使用,产生的客户端代码也更易于阅读 。
一个类只能有一个带有指定签名的构造器 。 编程人员通常知道如何避开这一限制 : 通过提供两个构造器,它们的参数列表只在参数类型的顺序上有所不同 。 实际上这并不是个好主意。面对这样的 API,用户永远也记不住该用哪个构造器 ,结果常常会调用错误 的构造器 。并且在读到使用了这些构造器的代码时,如果没有参考类的文档,往往不知所云 。
由于静态工厂方法有名称,所以它们不受上述限制 。 当一个类需要多个带有相同签名的构造器时,就用静态工厂方法代替构造器,并且仔细地选择名称以便突出静态工厂方法之间的区别 。
2、静态工厂方法不必在每次调用它们的时候都创建一个新对象。
不可变类可以使用预先构建好的实例,或者将构建好的实例缓存起来,进行重复利用,从而避免创建不必要的重复对象 。Boolean.valueOf (boolean )方法说明了这项技术 :它从来不创建对象 。 这种方法类似于享元(Flyweight)模式。
3、静态工厂方法可以返回原返回类型的任何子类型的对象。
这种灵活性的一种应用是 ,API 可以返回对象,同时又不会使对象的类变成公有的。以这种方式隐藏实现类会使 API 变得非常简洁 。 这项技术适用于基于接口的框架,因为在这种框架中,接口为静态工厂方法提供了自然返回类型 。
4、静态工厂方法所返回的对象的类可以随着每次调用而发生变化,这取决于静态工厂方法的参数值 。
只要是已声明的返回类型的子类型,都是允许的 。 返回对象的类也可能随着发行版本的不同而不同 。
EnumSet 没有公有的构造器,只有静态工厂方法 。 在 OpenJDK 实现中,它们返回两种子类之一的一个实例,具体则取决于底层枚举类型的大小:如果它的元素有 64个或者更少,就像大多数枚举类型一样,静态工厂方法就会返回一个 RegalarEumSet 实例,用单个 long 进行支持;如果枚举类型有 65 个或者更多元素,工厂就返回 JumboEnumSet实例,用一个 long 数组进行支持 。
5、静态工厂方法所返回的对象所属的类,在编写包含该静态工厂方法的类时可以不存在 。
这种灵活的静态工厂方法构成了服务提供者框架( Service Provider Framework)的基础,例如 JDBC(Java 数据库连接)API 。 服务提供者框架是指这样一个系统:多个服务提供者实现一个服务,系统为服务提供者的客户端提供多个实现,并把它们从多个实现中解稠出来 。
静态工厂方法的主要缺点
1、类如果不含公有的或者受保护的构造器,就不能被子类化 。
例如,要想将 Collections Framework 中的任何便利的实现类子类化 , 这是不可能的 。但是这样也许会因祸得福,因为它鼓励程序员使用复合( composition ),而不是继承。
2、静态工厂方法的第二个缺点在于,程序员很难发现它们。
在 API 文档中,它们没有像构造器那样在 API 文档中明确标识出来,因此对于提供了静态工厂方法而不是构造器的类来说,要想查明如何实例化一个类是非常困难的 。
静态工厂方法 的一些惯用名称
1、from一一类型转换方法,它只有单个参数,返回该类型的一个相对应的实例。
例如:Date d = Date.from(instant)
2、of——聚合方法,带有多个参数,返回该类型的一个实例,把它们合并起来。
例如:Set faceCards = EnumSet. of(JACK,QUEEN, KING) ;
3、valueOf一一比 from 和 of 更烦琐的一种替代方法。
例如:BigInteger prime = BigInteger .valueOf(Integer . MAX_ VALUE);
4、instance 或者 getInstance——返回的实例是通过方法的(如有)参数来描述的,但是不能说与参数具有同样的值。
例如:StackWalker 1uke = StackWalker . getInstance (options) ;
5、create 或者 newInstance——象 instance 或者 getinstaηce 一样,但 create或者 newInstance能够确保每次调用都返回一个新的实例。
例如:Object newArray = Array. newInstance(class0bject,arrayLen) ;
简而言之,静态工厂方法和公有构造器都各有用处,我们需要理解它们各自的长处 。静态工厂经常更加合适,因此切忌第一反应就是提供公有的构造器, 而不先考虑静态工厂 。