Effective Item 1 – 考虑用静态工厂方法代替构造器

我们有两种常见的方法获得一个类的实例。

·公有的构造器

·提供静态工厂方法(static factory method)

这一篇主要说明静态工厂方法相对于公有构造器的优劣。

相对公有的构造器,静态工厂方法有以下几大优势。

优势1.静态工厂方法的名称,因此比构造器更准确地描述返回的实例。

比如BigInteger.probablePrime方法:

publicstaticBigIntegerprobablePrime(intbitLength,Randomrnd){if(bitLength<2)thrownewArithmeticException(“bitLength<2”);//Thecutoffof95waschosenempiricallyforbestperformancereturn(bitLength<SMALL_PRIME_THRESHOLD?smallPrime(bitLength,DEFAULT_PRIME_CERTAINTY,rnd):largePrime(bitLength,DEFAULT_PRIME_CERTAINTY,rnd));}

顺便也贴出其调用的largePrime方法:

privatestaticBigIntegerlargePrime(intbitLength,intcertainty,Randomrnd){BigIntegerp;p=newBigInteger(bitLength,rnd).setBit(bitLength-1);p.mag[p.mag.length-1]&=0xfffffffe;//UseasievelengthlikelytocontainthenextprimenumberintsearchLen=(bitLength/20)*64;BitSievesearchSieve=newBitSieve(p,searchLen);BigIntegercandidate=searchSieve.retrieve(p,certainty,rnd);while((candidate==null)||(candidate.bitLength()!=bitLength)){p=p.add(BigInteger.valueOf(2*searchLen));if(p.bitLength()!=bitLength)p=newBigInteger(bitLength,rnd).setBit(bitLength-1);p.mag[p.mag.length-1]&=0xfffffffe;searchSieve=newBitSieve(p,searchLen);candidate=searchSieve.retrieve(p,certainty,rnd);}returncandidate;

虽然smallPrime和largePrime最后都是通过公有构造器返回实例。

但是如果仅仅用构造器重载表达这个实例的特征,这很难让人记住什么时候应该调用什么构造器。

而提供一个名称去描述实例更为直观。

优势2.静态工厂方法不必每次都创建一个新的对象,我们可以对实例进行控制。

这样我们就能将创建好的实例缓存起来重复利用,尤其是在创建对象的代价较高的情况下。

比如Boolean.valueOf:

publicstaticfinalBooleanTRUE=newBoolean(true);publicstaticfinalBooleanFALSE=newBoolean(false);publicstaticBooleanvalueOf(booleanb){return(b?TRUE:FALSE);}

优势3.静态工厂方法可以返回原返回类型的子类型对象。

这一点能体现静态工厂方法的灵活性,

以EnumSet为例(秦小波老师的<改善Java程序的151个建议>一书中也提到了这一点,即建议枚举项不要超过64个):

/***Createsanemptyenumsetwiththespecifiedelementtype.**@paramelementTypetheclassobjectoftheelementtypeforthisenum*set*@throwsNullPointerExceptionif<tt>elementType</tt>isnull*/publicstatic<EextendsEnum<E>>EnumSet<E>noneOf(Class<E>elementType){Enum[]universe=getUniverse(elementType);if(universe==null)thrownewClassCastException(elementType+”notanenum”);if(universe.length<=64)returnnewRegularEnumSet<>(elementType,universe);elsereturnnewJumboEnumSet<>(elementType,universe);}

而RegularEnumSet和JumboEnumSet为EnumSet的子类,并且都没有提供公有构造器。

优势4.静态工厂方法创建参数化(泛型)实例的时候更加简洁。

举个例子:

publicstatic<K,V>HashMap<K,V>newInstance(){returnnewHashMap<K,V>();}

这样一来创建实例时就可以:

Map<String,List<Integer>>n=newInstance();

而不是

Map<String,List<Integer>>m=newHashMap<String,List<Integer>>();

从Java7开始这一点变得没有意义,,事实上Josh Bloch也在书上提到了这点——Java以后会在构造器和方法调用中执行这种类型推导。

说说静态工厂方法的缺点。

缺点1.类如果不含公有或者受保护的构造器就不能被子类化。

所以上面说的[静态工厂方法可以返回原返回类型的子类型对象。]并不完全正确。

虽然我们可以通过复合方式(composition)解决这一问题,但这样两个类就不是[is-a]关系了。

缺点2.静态工厂方法的本质还是静态方法,他没有一个特别的标准。

我们无法在API文档中把一个静态工厂方法特别标识出来(也许可以加个标准annotation?)。

当我要从API中找一个方法去实例化一个类时,相对构造器而言还是不够直观。

虽然没有特个特别的标准,但我们也可以用标准的命名来弥补一点点。

比如valueOf,getInstance,newInstance,newType等…

本文出自 “Alvez. 99.9% 0B/s” 博客,请务必保留此出处

力微休负重,言轻莫劝人。

Effective Item 1 – 考虑用静态工厂方法代替构造器

相关文章:

你感兴趣的文章:

标签云: