在Java中调用多个方法

时间:2011-10-26 23:28:07

标签: java class methods method-chaining

我找到了一种在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类中的caloriessodiumcarbohydrate方法需要返回this?那个班级的地址在哪里?

谢谢!

4 个答案:

答案 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

Here's an SO question with some good examples.

答案 1 :(得分:3)

这通常被称为方法链接,它是OO的一个非常常见的想法:

http://en.wikipedia.org/wiki/Method_chaining

答案 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被非原子地调用(当对象被共享时这是不可取的多个线程),第三个因为caloriessodiumcarbohydrates字段强制是非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();