注:本系列對Effective Java各條進行整理和簡潔化,經過書籍和網上博客進行整合,主要參考http://www.jianshu.com/u/3b386b0ef996博主的系列。java
首先咱們先看下一個類RandomIntBuilder,用來建立隨機的Int整數dom
public class RandomIntBuilder { private final int min; private final int max; public int next() {...} }
接下來咱們來加入幾個構造器來看看ui
//沒任何問題 public RandomIntBuilder(int min, int max){ this.min = min; this.max = max; } //沒任何問題 //產生一個介於min到Integer.MAX_VALUE的值 public RandomIntBuilder(int min){ this.min = min; this.max = Integer.MAX_VALUE; } //編譯錯誤Duplicate method RandomIntBuilder(int) in type RandomIntBuilder //產生一個介於Integer.MIN_VALUE到max的值 public RandomIntBuilder(int max){ this.min = Integer.MIN_VALUE; this.max = max; }
從上面很明顯能夠看出,由於構造器區別在於參數類型、個數、順序不一樣而已,上面的第三個和第二個構造方法並無這些不一樣,所以沒法區別才致使報錯。這時候,咱們幸虧有靜態工廠方法,咱們能夠經過使用簡單的公共靜態方法返回一個類的實例。在Java中,你必定見過Boolean.valueOf吧,就像下面這樣:this
public static Boolean valueOf(boolean b) { return (b ? TRUE : FALSE); }
所以,咱們將靜態工廠方法用在這裏,上面的類就能夠變成以下寫法:spa
public class RandomIntBuilder { private final int min; private final int max; private RandomIntBuilder (int min, int max) { this.min = min; this.max = max; } public static RandomIntBuilderbetween(int max, int min) { return new RandomIntBuilder (min, max); } public static RandomIntBuilderForMin(int min) { return new RandomIntBuilder (min, Integer.MAX_VALUE); } public static RandomIntBuilderForMax(int max) { return new RandomIntBuilder (Integer.MIN_VALUE, max); } public int next() {...} }
同時,使用靜態工廠方法還可讓返回對象的類型能夠是返回類型的任意子類型。這使你隨意更改返回類型而不用擔憂影響到客戶端代碼成爲了可能。經過下面的一個例子來講明。咱們先定義一個接口,這個接口能夠產生其餘數據類型如String, Double或者Long的隨機生成器code
public interface RandomBuilder<T> {
T next();
}
下面咱們來編寫下Integer和String的生成器對象
class RandomIntBuilder implements RandomBuilder<Integer> { private final int min; private final int max; RandomIntBuilder (int min, int max) { this.min = min; this.max = max; } public Integer next() {...} }
class RandomStringBuilder implements RandomBuilder<String> { private final String prefix; RandomStringBuilder (String prefix) { this.prefix = prefix; } public String next() {...} }
能夠發現,上面的全部的類及類的構造器被定義爲包權限。也就是說除了本包以外的客戶端代碼沒法建立這些生成器的實例,那麼該怎麼辦呢?其實咱們只須要提供一個給外面的公共類就能夠了blog
public final class RandomBuilderCreator { private RandomBuilderCreator () {} public static final RandomBuilder<Integer> getIntBuilder() { //返回了子類型 return new RandomIntBuilder(Integer.MIN_VALUE, Integer.MAX_VALUE); } public static final RandomBuilder<String> getStringBuilder() { //返回了子類型 return new RandomStringBuilder(""); } }
好了,這就靜態工廠方法代替構造器的幾點好處了,如今讓咱們總結下靜態工廠方法和構造器的優劣對比接口
優點:get
①靜態工廠方法與構造器不一樣的第一大優點在於它們有名稱,能更好理解意思。
②靜態工廠方法與構造器不一樣的第二大優點在於沒必要在每次調用它們的時候都建立一個新對象。就像Boolean,咱們不須要建立Boolean對象來獲取b的值,而只要調用Boolean.valueOf(b)便可。
③靜態工廠方法與構造器不一樣的第三大優點在於它們能夠返回原返回類型的任意子類型對象。
④靜態工廠方法與構造器不一樣的第三大優點在於建立參數化實例的時候可使代碼變得更加簡潔。就像下面的代碼
Map<String, List<String>> map = new HashMap<String, List<String>>();
這顯得很繁瑣,若是HashMap有了如下靜態工廠方法,java會進行類型推導,將代碼簡潔化了爲:Map<String, List<String>> map = HashMap.newInstance();
public static <K,V> HashMap<K,V> newInstance(){
return new HashMap<K,V>();
}
劣勢:
①靜態工廠方法的主要缺點在於類若是不含有公有的或者受保護的構造器,就不能被子類化了。
②靜態工廠方法的第二個缺點是他們與其餘的靜態方法實際上沒有任何區別。所以在API中沒有像構造方法那樣唄明確標識出來,找起來會比較困難繁瑣。