我找到了一种在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
?那个班级的地址在哪里?
谢谢!
答案 0 :(得分:11)
它没有“进入”任何东西。
这些方法返回一个值。在这种情况下,它们返回当前实例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
答案 1 :(得分:3)
这通常被称为方法链接,它是OO的一个非常常见的想法:
答案 2 :(得分: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”,其次是因为setter被非原子地调用(当对象被共享时这是不可取的多个线程),第三个因为calories
,sodium
和carbohydrates
字段强制是非final
。在Builder
变体中,可以轻松地将这些字段设为final
,因为它们仅在构造函数中设置一次。
这种模式,每次调用Builder
都会返回对自身的引用,允许您链接这些调用以使它们易于阅读,并使结果对象的构造保持原子。
答案 3 :(得分:1)
您已将Builder
定义为NutritionFacts
的嵌套类。由于它是静态的,因此不需要存在NutritionFacts
实例。如果它不是静态的(所谓的“内部类”),则需要营养因子存在。此外,您的某些Builder字段会隐藏某些NutritionFact字段,但现在情况并非如此。
现在,既然你已经使用过这个嵌套类的东西,那么你不能只把它称为Builder
。您必须将其称为NutritionFacts.Builder
。因此,当您在第二个代码提取中执行new NutritionFacts.Builder(240, 8)
时,您获得的是一个具有serveSize 240和8个服务的新Builder实例。 NutritionFacts还没有真正发挥作用,只有这个名称才有用。
可以将新创建的实例分配给某个变量,或者可以通过调用某个方法立即使用它。这就是你正在做的事情,即你在它上面调用.calories(100)
来设置那个Builder的卡路里字段。但是如果你去看看那个方法,你会发现它有返回类型的Builder,它最终返回的是this
。 this关键字只是引用当前实例:再次使用相同的Builder。
然后继续.sodium(35)
和.carbohydrate(27)
,然后您最终在构建器上调用.build()
。看看那个方法。它调用NutritionFacts构造函数。该构造函数将Builder实例作为参数,我们的Builder将自己传入(再次使用this
)。现在我们终于获得了NutritionFacts实例,它是使用存储在Builder实例中的值创建的。
就像Jere所说,设置Builder营养素的方法使用方法链接方法返回它们被调用的对象,这样多个方法可以方便地链接在一条线上。实际上,你的第二个摘录也可以这样写:
NutritionFacts.Builder builder = new NutritionFacts.Builder(240, 8);
builder.calories(100);
builder.sodium(35);
builder.carbohydrate(27);
NutritionFacts cocaCola = builder.build();