Effective Java -- 用静态工厂方法来代替构造器

Posted 十木禾

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Effective Java -- 用静态工厂方法来代替构造器相关的知识,希望对你有一定的参考价值。

本文是 《Effective Java》的读书笔记,由于是Java进阶书,难免会有理解的偏差,发现有错误,非常欢迎能提出来,本人不胜感激!


为什么要考虑使用静态工厂方法代替构造器呢?本书中说明了以下的优点:

(1)静态工厂方法能提供一个可读性很高的名称来帮助你理解你要返回的对象是什么

利用书上的例子,在类型BigInteger中提供了一种构造函数:

    public BigInteger(int bitLength, int certainty, Random rnd) 
        //……
    

这个构造函数很大概率返回的是一个素数(概率为( 1(2100)

但是这个名字基本不能看出来他返回的可能为素数,于是利用静态工厂方法来表示,那就容易理解多了:

    public static BigInteger probablePrime(int bitLength, Random rnd) 
        //……
    

在前台我们写的代码的可读性是不是强多了:

BigInteger bi1=BigInteger.probablePrime(4,new Random());

(2)可以避免创建不必要的重复对象

如果我们需要经常的创建相同的对象,在创建对象的代价很高的时候,这个技术可以很好的去提升性能。

如下,我们看一下Boolean.valueOf(boolean)方法

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

这样就使得我们程序每次使用的都是我们我们预先创建好的实例,达到重复利用的效果。

在实现Singleton的时候,我们就使用了静态工厂方法,如下:

class MySingleton

    private static MySingleton mySingleton=null;

    public static MySingleton newInstance()
        if(mySingleton==null)
            MySingleton.mySingleton=new MySingleton();
        
        return mySingleton;
    

[ 注 ]:以上只是最基本的单例,如需了解更多,欢迎访问:实现线程安全的Singleton


(3)可以返回原返回类型的任意子类型

在基于接口编程的框架中,如果我们不确定该接口要返回哪一种实例化的类(如果要根据相应的参数信息来判断),就可以使用静态工厂方法了。
举个简单的例子,如下代码

        Map<Object,Object> hashMapObj=new HashMap<Object, Object>();
        Map<Object,Object> treeMapObj=new TreeMap<Object, Object>();

一个Map接口可以实例化为HashMapTreeMap等几种实例化类,如果我们需要根据传入的参数的信息判断该返回HashMap还是TreeMap,就可以写出如下代码:

Map<Object,Object> hashMapObj=Map.getInstance(Object obj);

然后再在 Map 类中写如下的静态工厂方法

public static<K,V> Map<K,V> getInstance(Object obj)
    if(/*参数obj满足某某条件*/) 
        return new HashMap<K, V>();
    
    if(/*参数obj满足某某条件*/) 
        return new TreeMap<K, V>();
    
    /*……*/
    /*等等等等*/

上面的程序当然只是我的假想,当然你可以把这个思想用到自己构造的接口中去。

那么在Java有没有这样的用法呢?当然是有的,我们来看一下EnumSet中的noneOf()方法,如下

    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)  //如果长度小于64,则返回RegularEnumSet类型
            return new RegularEnumSet<>(elementType, universe);
        else                        //反之,返回JumboEnumSet类型
            return new JumboEnumSet<>(elementType, universe);
    

在这个静态工厂方法中,就通过判断传入的底层枚举类型的大小(元素的个数),来选择返回的实例(如上代码注释)。


(4)在创建参数化实例的时候,可以使得代码更加的灵活

例如在创建一个Map<K,V>实例的时候,经常会使用如下代码:

Map<Object, Object> hashMapObj = new HashMap<Object, Object>();

如果Object类型又是一个复杂类型,这样循环,代码就会变得越来越冗长,如果HashMap提供了如下静态的工厂方法,则就简化代码了。(事实上没有)

public static<K,V> HashMap<K,V> getInstance()
    return new HashMap<K,V>();

然后在创建一个Map<K,V>实例的时候就可以使用下面简化的代码了:

Map<Object, Object> hashMapObj = HashMap.getInstance();

当然,在更高的JDK版本中,可以直接使用下面的写法

Map<Object, Object> hashMapObj = new HashMap();

套用一句以前常说的一句话:Every coin has two sides
那我们来看一下 静态工厂方法有哪些缺点吧

(1)一个类如果不含共有的或者受保护的构造器,就不能被子类化(即不能被其他的类继承)

(2)对于提供了静态工厂方法来实例化类的类,我们在查明相应方法的时候,会有一点点的困难。
在遵循一定的命名规范的时候,可以弥补这一缺点,如下的一些管用名称:

名称实例
valueOfBoolean.valueOf()
ofEnumSet.of()
getInstanceCalendar.getInstance()
newInstanceDocumentBuilderFactory.newInstance()
getTypeCharacter.getType()
newType尚未邂逅……

静态工厂方法和构造器各有用处,静态工厂通常更加合适,在实际编程中要多多考虑。
教学视频链接:Effective Java 之 用静态工厂方法来代替构造器

以上是关于Effective Java -- 用静态工厂方法来代替构造器的主要内容,如果未能解决你的问题,请参考以下文章

Effective Java 读书笔记之一 创建和销毁对象

Effective Java 学习笔记之创建和销毁对象

[读书笔记]Effective Java 第二章

Effective Java读后感

Effective Java读书笔记创建和销毁对象:考虑使用静态工厂方法代替构造器

重拾Effective Java一