天天看点

【Effective Java】条1:考虑用静态工厂方法代替构造器

在日常编程中,获取类的实例通常采用构造器的方法。还有另一种方法叫作“静态工厂方法(Static Factory Method)”。譬如Boolean类中这段代码:

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

使用静态工厂方法优点

  1. 静态工厂方法有名称

    构造函数的方法名比较单调,只能与类名相同。特别是当有多个构造函数时,有可能不能很好的区分彼此之间的参数区别。而静态方法为普通的方法,名字可以依据方法的作用来设定。

    譬如类java.util.concurrent.Executors,其中的静态方法如下:

    //创建一个包含固定数量线程的线程池
    public static ExecutorService newFixedThreadPool(int nThreads)
    //创建一个线程池,使线程能支持设定等级的并发
    public static ExecutorService newWorkStealingPool(int parallelism)
               
  2. 静态工厂方法可以每次不必创建新对象

    每次调用构造函数都需要创建新对象。但是在静态工厂方法中,则可以返回缓存的对象或者预先构建的对象。譬如最上面的

    public static Boolean valueOf(boolean b)

    代码。
  3. 静态工厂方法的返回类型可以为子类型对象

    这种灵活性有很多好处。

    • 静态工厂方法的返回对象的类可以不用是

      public

      的,这样隐藏了类的实现细节

      大家可以查看

      Collections

      类的代码,里面提供了很多的静态工厂方法,如:
      public class CollectionDemo {
      
        public static void main(String[] args) {
          //list1为同步列表
          List<Object> list1 = Collections.synchronizedList(Lists.newArrayList());
          //list2为不可改变的列表
          List<Object> list2 = Collections.unmodifiableList(Lists.newArrayList());
      
          //list1和list2虽都为列表,但也不是同种列表,list1为同步列表,list2则为不可改变的列表
        }
      }
                 
    • 静态工厂方法的返回对象的类的实现是可以随着版本迭代进行修改的

      在实际开发中大家都有迭代的痛苦,如果某个接口修改了实现也需要调用方进行修改,那是很痛苦的。这里我们可以参考

      EnumSet

      查看

      EnumSet

      的代码发现,其没有开放的构造方法,仅提供许多静态工厂方法,如下:
      方法 描述

      static <E extends Enum<E>> EnumSet<E> allOf(Class<E> elementType)

      创建包含

      elementType

      所有元素的

      EnumSet

      对象

      EnumSet<E> clone()

      返回当前

      EnumSet

      对象的复制结果

      static <E extends Enum<E>> EnumSet<E> complementOf(EnumSet<E> s)

      创建包含

      elementType

      对象,且包含

      s

      中的所有元素

      static <E extends Enum<E>> EnumSet<E> copyOf(Collection<E> c)

      创建

      EnumSet

      对象,且用

      c

      中的元素进行初始化

      static <E extends Enum<E>> EnumSet<E> copyOf(EnumSet<E> s)

      创建

      EnumSet

      对象,且用

      s

      中的元素进行初始化

      static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType)

      用指定的类型创建空的

      EnumSet

      对象

      static <E extends Enum<E>> EnumSet<E> of(E e)

      创建

      EnumSet

      对象,并用

      e

      初始化

      static <E extends Enum<E>> EnumSet<E> of(E first, E... rest)

      创建

      EnumSet

      对象,并用

      first

      rest

      元素初始化

      static <E extends Enum<E>> EnumSet<E> of(E e1, E e2)

      创建

      EnumSet

      对象,并用

      e1

      e2

      元素初始化

      static <E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3)

      创建

      EnumSet

      对象,并用

      e1

      e2

      e3

      元素初始化

      static <E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3, E e4)

      创建

      EnumSet

      对象,并用

      e1

      e2

      e3

      e4

      元素初始化

      static <E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3, E e4, E e5)

      创建

      EnumSet

      对象,并用

      e1

      e2

      e3

      e4

      e5

      元素初始化

      static <E extends Enum<E>> EnumSet<E> range(E from, E to)

      创建

      EnumSet

      对象,并用

      from

      to

      之间的元素初始化
      以EnumSet类型中的noneOf方法为例说明,当

      elementType

      的元素个数小于64时,则调用

      RegularEnumSet

      ,否则调用

      JumboEnumSet

      。当在以后的迭代中,

      RegularEnumSet

      的性能足以支撑超过64个元素时,我们完全可以删除调用

      JumboEnumSet

      的实现,而不会影响调用方。
      public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> var0) {
          Enum[] var1 = getUniverse(var0);
          if (var1 == null) {
              throw new ClassCastException(var0 + " not an enum");
          } else {
              return (EnumSet)(var1.length <=  ? new RegularEnumSet(var0, var1) : new JumboEnumSet(var0, var1));
          }
      }
                 
    • 静态工厂方法的返回对象的类,在编写的时候可以不必存在,也即服务提供者框架。
  4. 静态工厂方法在创建参数化类型实例时,使代码更简洁。这在高版本的

    JDK

    中已经不存在。

    此处的意思是,之前在创建某些类时,譬如

    Map<String, List<String>> map = new HashMap<String, List<String>>()

    ,片段

    String, List<String>

    需要重复写两次,在高版本

    JDK

    中已经可以不用这样写了,可以写为

    Map<String, List<String>> map = new HashMap()

使用静态工厂缺点

  1. 类若不包含公有的或受保护的构造器,则不能被子类化
  2. 与其他静态方法相比没有区别。意思是指用户可能不明白此静态工厂方法是用来实例化对象的,没有构造函数那么直白。平时编程中,尽量采用易懂的方法命名来表示,如

    newInstance

    getInstance

    getType

    newType

    valueOf

    of

    这样的命名

继续阅读