2011-10-26 20 views
5

多種方法,我發現調用Java的多種方法中的一種新的方式,我真的不明白髮生了什麼事背後:調用Java中

public class NutritionFacts { 
private final int servingSize; 
private final int servings; 
private final int calories; 
private final int fat; 
private final int sodium; 
private final int carbohydrate; 

public static class Builder { 
    // Required parameters 
    private final int servingSize; 
    private final int servings; 
    // Optional parameters - initialized to default values 
    private int calories  = 0; 
    private int fat   = 0; 
    private int carbohydrate = 0; 
    private int sodium  = 0; 
    public Builder(int servingSize, int servings) { 
     this.servingSize = servingSize; 
     this.servings = servings; 
    } 
     public Builder calories(int val) 
      { calories = val;  return this; } 
     public Builder fat(int val) 
      { fat = val;   return this; } 
     public Builder carbohydrate(int val) 
      { carbohydrate = val; return this; } 
     public Builder sodium(int val) 
      { sodium = val;  return this; } 
     public NutritionFacts build() { 
      return new NutritionFacts(this); 
     } 
} 

    private NutritionFacts(Builder builder) { 
     servingSize = builder.servingSize; 
     servings  = builder.servings; 
     calories  = builder.calories; 
    } 

} 

現在的類實例化使用這一行,這裏就是它得到混淆:

NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8).calories(100).sodium(35).carbohydrate(27).build(); 

這一切都有道理,直到NutritionFacts.Build(int,int),在那之後,究竟發生了什麼?爲什麼Builder類中的calories,sodium,carbohydrate方法需要返回this?那個班級的地址在哪裏?

謝謝!

回答

9

它不會「進入」任何東西。

這些方法返回一個值。在這種情況下,他們返回當前實例this。該實例有方法,如calories()carbohydrates()

foo.calories(12)返回實例,我們可以調用它的方法:foo.calories(12).sodium(35)

它與構造函數的「返回值」沒有什麼不同,隱式定義爲新的實例。在這種情況下,這是正常的方法,仍然返回一個實例 - 當前的實例。

這是一樣的:

Builder foo = new Builder(1, 2); // The "return" value of a ctor is the reference, foo 
foo.sodium(10); // Returns foo, but we ignore it 
foo.calories(42); // Returns foo, but we ignore it 

(foo.sodium(10)).calories(42); 
^^^^^^^^^^^^^^^^ foo, with the side effect of setting the sodium value 

Here's an SO question with some good examples.

+0

這是否意味着從foo.calories返回值(12),這是實例,用於調用線下一種方法,在我們的例子中,鈉(35)? –

+0

@ Cookie503絕對:) –

+0

@ Cookie503沒錯。 –

2

這種技術被稱爲「method chaining」,是一個非常好的風格熟悉。你這樣做,而不必這樣說:

NutritionFacts cocaCola = new NutritionFacts(240, 8, 100, 35, 27); 

...這是容易出現的錯誤,因爲在構造函數中的順序或參數的意義可能會改變。或者也代替這樣的:

NutritionFacts cocaCola = new NutritionFacts(240, 8); 
cocaCola.setCalories(100); 
cocaCola.setSodium(35); 
cocaCola.setCarbohydrates(27); 

...這是壞的,有幾個原因:第一,因爲它不是爲「fluent」在清晰度方面,第二是因爲制定者被稱爲非原子(這是當對象在多個線程中共享時是不合需要的);第三,因爲calories,sodiumcarbohydrates字段是強制不是final。在Builder變種中,這些字段很容易製作,因爲它們只在構造函數中設置一次。

這種模式,其中每次調用Builder時都會返回對自身的引用,這樣您可以鏈接這些調用以使它們易於閱讀,並將生成的對象的構造保持爲原子。

1

您已將Builder定義爲嵌套類NutritionFacts。由於它是靜態的,因此不需要存在NutritionFacts實例。如果它不是靜態的(即所謂的「內部階級」),則需要存在營養成分。此外,您的某些Builder字段將隱藏某些NutritionFact字段,但現在情況並非如此。

現在,既然您已經使用了這個嵌套類thingie,那麼您不能僅僅將其稱爲Builder。您必須將其稱爲NutritionFacts.Builder。因此,在第二次代碼提取時,如果執行new NutritionFacts.Builder(240, 8),則會得到一個新的具有servingSize 240和8份的Builder實例。 NutritionFacts並沒有真正發揮作用,它只是名稱的存在。

新創建的實例可以分配給某個變量,或者可以立即使用,例如調用某個方法。這就是你正在做的事情,即你可以調用.calories(100)來設置該Builder的卡路里字段。但是如果你去看看這個方法,你會注意到它有返回類型的Builder,它返回的結果是this。這個關鍵字只是簡單地引用了當前的實例:同樣的Builder也是。

然後.sodium(35).carbohydrate(27)也一樣,之後最終在Builder上調用.build()。看看這種方法。它調用NutritionFacts構造函數。該構造函數將Builder實例作爲參數,並且我們的Builder將自身傳入(再次與this)。現在我們終於得到了一個N​​utritionFacts實例,它使用存儲在Builder實例中的值創建。

就像Jere說的那樣,設置Builder營養素的方法使用方法鏈接方法來返回它們被調用的對象,這樣可以方便地將多個方法鏈接在一條線上。在現實中,你的第二個提取物也可以同樣這樣寫:

NutritionFacts.Builder builder = new NutritionFacts.Builder(240, 8); 
builder.calories(100); 
builder.sodium(35); 
builder.carbohydrate(27); 
NutritionFacts cocaCola = builder.build();