2011-10-26 38 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();