考虑用静态工厂方法替代构造器

2018/07/20

对于类而言,为了让客户端获得它自身的一个实例,最常用的方法就是提供一个公有的构造器,除了这种方式外,另外一种实现方式是为类提供一个公有的的静态工厂方法。也就是说,我们有以下两种方法可以创建类的实例:

  • 利用共有构造器
  • 类提供一个共有的静态工厂方法,它返回类的实例

公有构造器的缺点

  • 只能通过new className()的方式来创造实例
  • 每次调用必然产生一个新的对象
  • 返回类型就是该类

静态工厂方法的优点

  • 静态工厂具有名称

例如,构造器BigInteger(int,int,Random)返回的 BigInteger 可能为素数,但是如果用名为BigInteger.probablePrime的静态工厂方法来表示,显然更清楚。如果不理解的话请看下面这段代码:

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

public static void main(String[] args) {
        Boolean a = Boolean.valueOf(true);
        Boolean b = Boolean.valueOf(false);
        System.out.println(a);
        System.out.println(b);
}

你看你在创建ab这两个实例的时候,使用静态工厂方法,通过valueOf()就可以很直观的知道自己要做什么,因为一个类只能有一个带有指定签名的构造器,如果要提供多个构造器,则只能在参数上做文章。这样不利于编写文档。当然,输出的结果自然就是a=trueb=flase啦。

  • 不必在调用他们的时候都创建一个新的对象

我们可以通过预先创建好的对象实例,或者将构建好的实例缓存起来,进行重复利用,从而避免创建不必要的重复对象。Boolean.valueOf(boolean)方法说明了这项技术:它就从来都不创建对象。

  • 它们可以返回原类型的任何子类型的对象

考虑到一些私有的类我们不想对外界暴露,又要提供获得该类对象的方法,采用静态工厂方法的方式就可以实现。

  • Java 的泛型类在实例化时,还是需要写两次类型参数,非常冗长
Map<String,List<String>> m =
    new HashMap<String,List<String>>();

但是有了静态工厂方法,编译器就可以替你找到类型参数。这被称作类型推导(type inference)。例如,假设 HashMap 提供了这个静态工厂:

public static <K,V> HashMap<K,V> newInstance(){
    return new HashMap<K,V>();
}

那么现在,你就可以用下面这代代码代替上面那段冗长的申明。

Map<String,List<String>> m = HashMap.newInstance();

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

  • valueOf:返回的实例与它的参数具有相同的值
  • getInstance:返回的实例是通过方法的参数来描述的,但不一定就代表参数与返回值相同
  • newInstance:返回的实例的值与参数相同
  • getType:与getInstance功能相同,但处于不同的类中的使用
  • newType:与newInstance功能相同,但处于不同的类中的使用

静态工厂方法的缺点

  • 类如果不含有共有的或者受保护的构造器,就不能被子类化
  • 它们其实与其他的静态方法相比,在本质上没有任何区别

Post Directory