Mihai Neacsu
Mihai Neacsu

Reputation: 2115

Calling multiple methods in Java

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!

Upvotes: 7

Views: 38070

Answers (4)

Dave Newton
Dave Newton

Reputation: 160321

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.

Upvotes: 11

G_H
G_H

Reputation: 12019

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();

Upvotes: 1

mergeconflict
mergeconflict

Reputation: 8276

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.

Upvotes: 2

Jere
Jere

Reputation: 3397

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

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

Upvotes: 3

Related Questions