Java中调用多个方法

发布于 2024-12-12 06:18:06 字数 1656 浏览 0 评论 0原文

我发现了一种在 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 类中的卡路里、钠、碳水化合物方法需要返回 this?该类地址在哪里?

谢谢你!

I found a new way of calling multiple methods in Java and I don't really understands what's happening behind:

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;
    }

}

Now the class is instantiated using this line, and here's where it gets confusing:

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

It all makes sense till the NutritionFacts.Build(int, int), after that, what exactly is happening? Why do the calories, sodium, carbohydrate methods from Builder class need to return this? Where does that class adress go into?

Thank you!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

永言不败 2024-12-19 06:18:06

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

这些方法返回一个值。在本例中,它们返回当前实例 this。该实例具有一些方法,例如 calories()carboenchants()

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

这是一个带有一些很好示例的问题。

It doesn't "go into" anything.

These methods return a value. In this case, they return the current instance, this. That instance has methods, like calories() and carbohydrates().

foo.calories(12) returns the instance, and we can call its methods: foo.calories(12).sodium(35).

It's no different than the "return value" from the constructor, implicitly defined as the new instance. In this case it's normal methods, still returning an instance--the current one.

It's the same as this:

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.

羁客 2024-12-19 06:18:06

这通常称为方法链接,并且是 OO 中非常常见的想法:

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

This is generally known as method chaining and it is a pretty common idea across OO:

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

路弥 2024-12-19 06:18:06

这种技术称为“方法链接”,是一种非常适合熟悉的风格。您这样做,而不必说类似:

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

... 这很容易出现错误,因为构造函数中参数的顺序或含义可能会改变。或者也可以代替类似的内容:

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

...这有几个原因,这很糟糕:首先是因为它不像“流利”在易读性方面,第二是因为 setter 是非原子调用的(当对象在多个线程之间共享时这是不希望的),第三是因为卡路里、钠 和碳水化合物字段强制为非最终。在 Builder 变体中,这些字段可以轻松地成为 final,因为它们仅在构造函数中设置一次。

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

This technique is called "method chaining", and is a very nice style to get familiar with. You do this instead of having to say something like:

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

... which is prone to bugs, because the order or meaning of arguments in the constructor might change. Or also instead of something like:

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

... which is bad for a few reasons: first because it's not as "fluent" in terms of legibility, second because the setters are called non-atomically (which is undesirable when the object is shared across multiple threads), and third because the calories, sodium and carbohydrates fields are forced to be non-final. In the Builder variant, those fields can easily be made final because they are only set once in the constructor.

This pattern, in which every call to the Builder returns a reference to itself, lets you chain those calls to keep them easily readable, and to keep the construction of the resulting object atomic.

终止放荡 2024-12-19 06:18:06

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

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

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

对于 .sodium(35).carboenchant(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();

You've defined Builder as a nested class of NutritionFacts. Since it's static, it doesn't require a NutritionFacts instance to exist. If it wasn't static (a so-called "inner class") it would require a NutritionFacts to exist. Also, some of your Builder fields would be hiding certain NutritionFact fields, but that isn't the case now.

Now, since you've used this nested class thingie, you can't just refer to it as Builder. You'll have to refer to it as NutritionFacts.Builder. So when in your second code extract you do new NutritionFacts.Builder(240, 8), what you get is a new Builder instance with a servingSize 240 and 8 servings. NutritionFacts doesn't really come into play yet, it's only there for the name.

That newly create instance could be assigned to some variable, or it could be used immediately by, say, calling some method on it. That's what you're doing, namely you call .calories(100) on it which sets the calories field of that Builder. But if you go and take a look at that method, you'll notice it has return type Builder and what it ends up returning is this. The this keyword simply refers to the current instance: that very same Builder yet again.

The same then goes on for .sodium(35) and .carbohydrate(27), after which you end up calling .build() on your Builder. Look at that method. It calls the NutritionFacts constructor. That constructor takes a Builder instance as an argument, and our Builder passes itself in (again with this). Now we're finally getting a NutritionFacts instance, and it is created using the values that were stored in the Builder instance.

Like Jere said, the methods that set the Builder nutrients use the method chaining approach to return the object they were called on, so that multiple methods can be conveniently chained together on a single line. In reality, your second extract could just as well be written like this:

NutritionFacts.Builder builder = new NutritionFacts.Builder(240, 8);
builder.calories(100);
builder.sodium(35);
builder.carbohydrate(27);
NutritionFacts cocaCola = builder.build();
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文