【问题标题】:Calling multiple methods in Java在 Java 中调用多个方法
【发布时间】:2011-12-16 03:27:20
【问题描述】:

我发现了一种在 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?那个班级地址在哪里?

谢谢!

【问题讨论】:

    标签: java class methods method-chaining


    【解决方案1】:

    它不会“进入”任何东西。

    这些方法返回一个值。在这种情况下,它们返回当前实例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.

    【讨论】:

    • 这是否意味着来自 foo.calories(12) 的返回值,即实例,用于调用行中的下一个方法,在我们的例子中,是 sodium(35)?
    • 非常感谢您的回答!
    【解决方案2】:

    这通常被称为方法链,它是整个 OO 中非常普遍的想法:

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

    【讨论】:

      【解决方案3】:

      这种技术被称为“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”,其次因为设置器是非原子调用的(当对象在多个线程之间共享时这是不可取的),第三是因为caloriessodiumcarbohydrates 字段强制为非final。在Builder 变体中,这些字段可以很容易地设置为final,因为它们只在构造函数中设置一次。

      在这种模式下,对Builder 的每次调用都会返回对自身的引用,让您可以链接这些调用以使其易于阅读,并使生成的对象的构造保持原子性。

      【讨论】:

        【解决方案4】:

        您已将Builder 定义为NutritionFacts 的嵌套类。由于它是静态的,因此不需要存在 NutritionFacts 实例。如果它不是静态的(所谓的“内部类”),则需要 NutritionFacts 才能存在。此外,您的一些 Builder 字段会隐藏某些 NutritionFact 字段,但现在情况并非如此。

        现在,既然你已经使用了这个嵌套类的东西,你不能只将它称为Builder。您必须将其称为NutritionFacts.Builder。因此,当您在第二个代码提取中执行 new NutritionFacts.Builder(240, 8) 时,您将得到一个新的 Builder 实例,其 servingSize 为 240 和 8 份。 NutritionFacts 还没有真正发挥作用,它只是为了名字而已。

        新创建的实例可以分配给某个变量,或者可以通过调用某个方法来立即使用它。这就是您正在做的事情,即您在其上调用.calories(100) 来设置该生成器的卡路里字段。但是如果你去看看那个方法,你会注意到它有返回类型 Builder 并且它最终返回的是this。 this 关键字只是指当前实例:又是同一个 Builder。

        .sodium(35).carbohydrate(27) 也是如此,然后您最终在 Builder 上调用 .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();
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-09-15
          • 2019-08-01
          • 1970-01-01
          • 2014-08-18
          • 1970-01-01
          • 2018-05-01
          • 2021-11-16
          • 2011-09-23
          相关资源
          最近更新 更多