2013-06-18 50 views
15

我有以下類:如何避免Java中的構造器代碼冗餘?

class Pair 
{ 
    String car; 
    Integer cdr; 

    public Pair() {} 
    public Pair (String car) { this.car = car; } 
    public Pair (Integer cdr) { this.cdr = cdr; } 

    public Pair (String car, Integer cdr) 
    { 
     this(car); 
     this(cdr); 
    } 
} 

類包含兩個可選值,我想提供一切可能的構造排列。第一個版本不初始化任何東西,第二個版本只初始化第一個值,第三個初始化第二個值。

最後的構造函數是第二個和第三個的組合。但是不可能寫下來,因爲代碼失敗。

 
constructor.java:13: call to this must be first statement in constructor 
     this(cdr); 
      ^
1 error 

是否有可能編寫沒有任何代碼冗餘的最後一個構造函數(也沒有調用相同的setter方法)?

+2

你和this()和super()的問題必須是[構造函數中的第一個語句] [1]。 [1]:http://stackoverflow.com/questions/1168345/why-does-this-and-super-have-to-be-the-first-statement-in-a-constructor – iMBMT

+0

您可以調用這個(...)構造函數兩次,只能有一個構造函數調用另一個構造函數,並且鏈接的構造函數調用必須是構造函數中的第一個語句。 – RudolphEst

回答

50

通常,參數較少的構造函數應該使用更多的參數來調用它們。

public Pair() {} 
public Pair(String car) { this(car, null); } 
public Pair(Integer cdr) { this(null, cdr); } 
public Pair(String car, Integer cdr) { this.car = car; this.cdr = cdr; } 
+12

一個選項將使字段'final'並讓默認構造函數調用'this(null,null);' –

+0

構造函數調用其父項的規則的例外是最終變量。對於具有最終變量的Pair版本,代碼如下所示: 'class pair {private final final String car; private final Integer cdr; public Pair(){this.car = null; this.cdr = null; } public pair(String car){this.car = car; this.cdr = null; } ... }' –

+3

@MichaelShopsin爲什麼會出現異常 – josefx

6
class Pair 
{ 
    String car; 
    Integer cdr; 

    public Pair() {} 
    public Pair (String car) { 
     this(car, null) 
    } 
    public Pair (Integer cdr) { 
     this(null, cdr); 
    } 

    public Pair (String car, Integer cdr) { 
     this.car = car; 
     this.cdr = cdr; 
    } 
} 
19

鏈的相反方向構造,最具體的是在一個設定的所有字段:

public Pair() { 
    this(null, null); // For consistency 
} 

public Pair(String car) { 
    this(car, null); 
} 

public Pair(Integer cdr) { 
    this(null, cdr); 
} 

public Pair (String car, Integer cdr) { 
    this.car = car; 
    this.cdr = cdr; 
} 

這樣:

  • 只有一個地方集字段,它設置所有字段
  • 從任何其他構造函數中,您可以指定(並告訴您何時讀取代碼)其他字段的「默認」值。

順便說一句,我強烈建議您將字段設置爲私有(可能是最終),併爲其賦予更多有意義的名稱。請注意,如果你有5個參數和一個帶3的構造函數,其中一個帶有4,另一個帶有5,那麼你可能會選擇連接3 - > 4 - > 5,或者你可以選擇連接3 - > 4 - > 5。直行從3 - > 5

此外,您可能要完全刪除單參數的構造函數 - 這將是更具可讀性有靜態方法來代替,在這裏你可以指定名字的含義:

public static Pair fromCar(String car) { 
    return new Pair(car, null); 
} 

public static Pair fromCdr(Integer cdr) { 
    return new Pair(null, cdr); 
} 

或者在我的意思的首選命名:

public static Pair fromFirst(String first) { 
    return new Pair(first, null); 
} 

public static Pair fromSecond(Integer second) { 
    return new Pair(null, second); 
} 

此時,您可以使Pair類具有通用性,而不用擔心在兩個類型參數相同的情況下將調用哪個構造函數。此外,讀取代碼的任何人都可以理解將構建什麼而不必檢查參數的類型。

+0

它們有一個含義:http://en.wikipedia.org/wiki/CAR_and_CDR – ceving

+0

@ceving:在我看來,這在Java語境中是一個相當模糊的含義......特別是因爲它們的意思是*運算符*而不是*值*。如果這些只是爲了「第一」和「第二」的價值觀,那麼這些名稱對於大多數人來說將更有意義。 –

+3

夥計們,我覺得討論'car'和'cdr'的命名是非常關鍵的,這裏有點吵;-) –

8

您可能在這裏尋找builder pattern

除此之外,這種模式允許你沒有一個bazillion構造函數來覆蓋所有的情況。對於你的情況,這可能是:

@Immutable // see JSR 305 
public final class Pair 
{ 
    private final String car; 
    private final integer cdr; 

    private Pair(final Builder builder) 
    { 
     car = builder.car; 
     cdr = builder.cdr; 
    } 

    public static Builder newBuilder() 
    { 
     return new Builder(); 
    } 

    // whatever other methods in Pair, including accessors for car and cdr, then: 

    @NotThreadSafe // see JSR 305 
    public final class Builder 
    { 
     private String car; 
     private int cdr; 

     private Builder() 
     { 
     } 

     public Builder withCar(final String car) 
     { 
      this.car = car; 
      return this; 
     } 

     public Builder withCdr(final int cdr) 
     { 
      this.cdr = cdr; 
      return this; 
     } 

     public Pair build() 
     { 
      return new Pair(this); 
     } 
    } 
} 

使用範例:

final Pair newPair = Pair.newBuilder.withCar("foo").withCdr(1).build(); 

優勢:Pair現在是一成不變的!

+0

不錯的和全面的建設者模式的例子,但在這種情況下可能有點矯枉過正。 (另外,不變性不會自動成爲我認爲的優點)。但仍然+1一個不錯的選擇! –

+2

@ Chips_100我已經習慣了這樣做,另一方面,我可能會錯過一些重要的觀點......例如,DI框架(Dependecy Injection)。 – fge