获取一个类的实例的传统方法是使用公开的构造器,除此之外,一个类还可以提供公开的静态工厂方法(static factory method)来返回它的实例。例如 Boolean
类中的 valueOf
方法,这个方法将基本类型 boolean
转换为一个 Boolean
对象的引用:
public static Boolean valueOf(boolean b) {
return b ? Boolean.TRUE : Boolean.FALSE;
}
注:静态工厂方法与设计模式中的工厂方法不同。
静态工厂方法的优点:
-
与构造器不同,静态工厂方法拥有自己的名字。考虑一个场景:
BigInterger
类的一个构造器BigInterger(int, int, Random)
可能返回一个素数,然而传统的构造器不能很直观地表达出来,我们可以用一个静态工厂方法BigInteger.probablePrime
使表达变得更清晰。此外,如果一个类需要几个参数类型相同的构造器,它只能打乱参数的顺序来进行区别,这使得类的构造变得十分困难。此时可以考虑使用静态工厂方法,这些方法拥有相同的参数类型,但你可以赋予它们精心挑选的名字,使它们的区别变得明显。
-
当静态工厂方法被调用时,它们不需要每一次都创建一个新的对象。如开篇的
Boolean.valueOf(boolean)
所示,这个方法从来不创建对象,它只是返回Boolean
类中已经创建的对象:public static final Boolean TRUE = new Boolean(true); public static final Boolean FALSE = new Boolean(false);
这有效地提升了程序的性能。这一技巧与设计模式中的享元模式(Flyweight)十分相似。
-
静态工厂方法可以返回其返回类型的任何子类的对象。一个可能的应用场景:API 可以返回对象,而不需要将它们的类公之于众,以这种方式隐藏实现类将使这个 API 变得十分紧凑。这个技术适合于基于接口的框架(interface-based frameworks)。
-
随着输入参数的不同,同一个静态工厂方法可以返回不同的子类。例如
EnumSet
类:public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) { Enum<?>[] universe = getUniverse(elementType); if (universe == null) throw new ClassCastException(elementType + " not an enum"); if (universe.length <= 64) return new RegularEnumSet<>(elementType, universe); else return new JumboEnumSet<>(elementType, universe); }
当底层枚举类型的元素不超过64个时,返回
RegularEnumSet
,超过64个则返回JumboEnumSet
。 -
在编写包含静态方法的类时,返回对象的类不需要存在。这种灵活的静态工厂方法构成了服务提供者框架(service provider frameworks)的基础,比如Java的数据库连接 API(JDBC)。
静态工厂方法的缺点
- 只提供静态工厂方法、不提供
public
或protected
构造器的类不能被继承(子类化)。这也可能因祸得福:它鼓励程序员使用组合而不是继承。 - 静态工厂方法很难被程序员找到。在API文档中,静态工厂方法不能像构造器那样突出,这使得程序员难以找到它们来实例化一个类。
一些静态工厂方法的常用名称
-
from
:一个类型转换方法,接受一个参数并返回这个类型的相应实例。Date d = Date.from(instant);
-
of
:一个聚合方法,接受多个参数、将它们合并在一起并返回这个类型的相应实例。Set<Rank> faceCards = EnumSet.of(JACK, QUEEN, KING);
-
valueOf
:from
和to
的更为详细的替代方式。BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE);
-
instance
或getInstance
:返回一个实例,该实例由其参数(如果有的话)描述,但不能说具有相同的值。StackWalker luke = StackWalker.getInstance(options);
-
create
或newInstance
:与instance
或getInstance
类似,除了该方法保证每个调用返回一个新的实例。Object newArray = Array.newInstance(classObject, arrayLen);
-
getType
:与getInstance
类似,当工厂方法在不同的类中时使用。Type 是工厂方法返回对象的类型。FileStore fs = Files.getFileStore(path);
-
newType
:与newInstance
类似,当工厂方法在不同的类中时使用。Type 是工厂方法返回对象的类型。BufferedReader br = Files.newBufferedReader(path);
-
type
:getType
和newType
的简洁版。List<Complaint> litany = Collections.list(legacyLitany);