天天看点

创建和销毁对象之用静态工厂方法代替构造器

Java 中获取某个类的实例最常用的方法是使用构造器,除此之外还有一种方法——静态工厂方法(static factory method)。这里的静态工厂方法是返回类的实例的静态方法,不同于设计模式中的工厂方法模式。下面是一个例子:

public static Boolean(boolean b){
return b ? Boolean.TRUE : Boolean.FALSE;
}
           

valueOf这个方法将 boolean 基本类型值转换为 Boolean 对象引用。

类可以通过静态工厂方法提供实例,静态工厂方法相比公有构造器,具有以下优势:

1. 有名称

静态工厂可以有适当的名字,这一点不同于构造器。当构造器的参数列表无法准确描述对象时,静态工厂的名字会使得代码具有更好的可读性。

当用户需要多个构造器时,通常的做法是提供参数列表不同的构造器,但是这种实现方式会令用户在调用时很难分清需要的是哪个,结果是经常调用错误。另一个缺陷是如果不查看注释或文档,用户无法知道这些构造器的区别。

静态工厂方法的名称可以有效解决上述问题,但一个类需要提供多个不同的构造器时,可以用静态工厂方法实现,这些方法的名字也可以清楚表达该方法的作用。

2. 不必每次调用时都创建一个新对象

静态工厂方法能够为重复的调用返回同一个对象,从而避免创建不必要的重复对象,提升性能。这使得能够很好的控制哪些实例该存在,同时令不可变类可以重复利用预先构建或缓存的实例,确保不会存在两个相等的实例,当且仅当 a==b 时才有 a.equals(b)为 true,这一点使客户端可以使用==代替 equals 方法比较两个对象,大大提升了性能。枚举类型就保证了这一点。

3. 可以返回原返回类型的任何子类型的对象

能够保证选择返回对象的类型的灵活性。这一点的一种应用是 API 返回对象不会要求对象的类必须公有。这项技术适用于接口的框架,接口会为静态工厂方法提供自然返回类型。又由于接口不能有静态方法,因此接口类Type(例如 Java Collections Framework)的静态工厂方法放在一个不可实例化类 Types (例如java.util.Collections)中。

公有的静态工厂方法除可以返回非公有类型的对象之外,还可以实现每次调用返回不同类型的对象,只要是已声明的返回类型的子类就可以。

另外,静态工厂方法返回对象的类在编写该静态工厂方法所在的类时可以不存在,这种静态工厂方法构成了服务提供者框架的基础,例如 JDBC API。

4. 创建参数化类型实例时,使代码更加简洁

在调用参数化类的构造器时,即使参数类型非常明显也必须指明,这通常要连续提供两次相同的参数类型:

Map<String,List<String>> map = 
      new HashMap<String,List<String>>();
           
如果类型参数数量较多时,代码会非常复杂。静态工厂方法使编译器自动找到类型参数,例如:
public static <K,V> HashMap<K,V> newInstance(){
      return new HashMap<K,V>();
}
           
声明时的代码会变得很简洁:
Map<String,List<String>> map = HashMap.newInstance();
           
Java1.7版本之后实现了在构造器调用和方法调用中执行这种类型推导。

静态工厂的功能如此强大,但与构造器相比,它还是有一定的缺陷的。

1. 类如果不包含 public 或 default 构造器,就不能被子类化。

公有静态工厂方法返回的非公有类也是如此。

2. 与其他的静态方法没有区别

静态工厂方法没有像构造器一样在 API 文档中被明确标识出来,因此要想通过静态工厂方法查明类如何实例化比较困难。

静态工厂方法的一些惯用名称:

valueOf、of、getInstance、newInstance、getType、newType。