创建对象最直接的想法就是通过new调用构造器,其实大多数情况下应当通过自己写一个静态公有方法,返回类的实例,比如下面这个方法:
1 | public static Boolean valueOf(boolean b) { |
和直接使用构造器相比,使用静态工厂方法具有以下优点:
- 静态工厂方法可以根据用途自己定义名称(不必与类相同),可读性更强
- 静态工厂方法可能不用在每次调用时都创建新对象
- 静态工厂方法可以返回声明类型的子类型的实例
下面分别阐述这三个优点。
静态工厂方法可以根据用途自己定义名称
构造器的方法名只能使用类名,如果有多个构造器,只能通过参数类型甚至顺序来区分,这样可读性非常差,而且不容易记,调用的时候很容易出错。
1 | public class TantanitReader { |
比如在上面的例子中,类TantanitReader表示我的官方博客tantanit.com的读者信息,其中age表示年龄,loginName表示登录名,realName表示真实姓名,career表示职业,除了age是int类型之外,其它几个都是字符串类型。
假设现在需要根据年龄和登录名,创建一个读者,使用构造器的话,代码如下:
1 | public TantanitReader(int age, String loginName) { |
假设又有一个场景,需要根据年龄(int类型)和真实姓名(String类型)创建读者,如果使用构造器的话,参数类型和上一个构造器相同,要解决这个问题呢?机智如你,一定想到通过对调参数顺序这个取巧(而不优雅)的方法,来规避这个问题。
1 | public TantanitReader(String realName, int age) { |
如果一个类要通过构造器创建读者,就需要根据这两个构造器的参数顺序记住各自的用法,是不是很容易记错啊。就算记住了也要查文档或者看看TantanitReader这个类的源文件,再确认一遍吧。
好了,又来了一个场景,需要根据年龄(int类型)和职业创建读者,如果使用构造器的话,相信聪明如你,也是没有办法了。
所以,应当使用静态工厂方法来代替构造器。下面的代码很好地解决了这个问题:
1 | public static TantanitReader getByAgeAndLoginName(int age,String loginName){ |
三个方法使用不同的名称,并且在名称中暗示了参数的顺序,这样,在调用的时候,就不容易出错,也不用再查看文档或构造器的源码了。
静态工厂方法可能不用在每次调用时都创建新对象
使用静态工厂方法,在有些使用场景下,可以重复使用一个提前生成的对象,或者从缓存中获取一个对象,而不用创建一个新的对象。文章开头的例子中的Boolean.valueOf(boolean)方法,调用时就不用创建新的对象。这样节省了内存开销,也提高了性能。同时,和每次都new一个新的对象,都是不同的对象相比,在这种重复使用的场景中,每次返回的对象都是严格意义相同的对象,可以做到对象级别的控制。这种控制对单例和不可实例化的使用场景很有用,并且可以放心的使用==代替equals方法,以提高性能。比如枚举类就使用了这样的技术。
静态工厂方法可以返回声明类型的子类型的实例
构造函数,只能返回该类的实例,不能返回该类的子类的实例,静态工厂方法不受这个限制,因此可以很好地使用Java语言的多态性。在方法声明返回值类型为父类型,甚至接口类型,而返回子类型或接口的某个类型的具体实现。这样,将来想返回其它子类型或接口的其它实现时,只要直接修改方法体,不用改方法的声明,从而不会影响调用方的使用。
小结
本文使用谈谈IT读者的例子介绍了使用静态工厂方法替代构造器的三个优点,在有些场景,这些优点会很明显,下次遇到了,记得考虑使用静态工厂方法哦!