Вызов нескольких методов в Java - PullRequest
7 голосов
/ 27 октября 2011

Я нашел новый способ вызова нескольких методов в 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), после этого, что именно происходит? Почему методы calories, sodium, carbohydrate из класса Builder должны возвращать this? Куда уходит этот классный адрес?

Спасибо!

Ответы [ 4 ]

11 голосов
/ 27 октября 2011

Ничего не "входит".

Эти методы возвращают значение. В этом случае они возвращают текущий экземпляр 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

Вот SO вопрос с некоторыми хорошими примерами.

3 голосов
/ 27 октября 2011

Это обычно называется цепочкой методов, и это довольно распространенная идея в ОО:

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

2 голосов
/ 27 октября 2011

Этот метод называется " метод цепочки " и является очень хорошим стилем для ознакомления. Вы делаете это вместо того, чтобы говорить что-то вроде:

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

... который подвержен ошибкам, потому что порядок или значение аргументов в конструкторе могут измениться. Или также вместо чего-то вроде:

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

... что плохо по нескольким причинам: во-первых, потому что это не так " свободно " с точки зрения разборчивости, во-вторых, потому что сеттеры называются неатомно (что нежелательно, когда объект совместно используется несколькими потоками), и в-третьих, поскольку поля calories, sodium и carbohydrates заставили быть не final. В варианте Builder эти поля можно легко сделать final, поскольку они устанавливаются в конструкторе только один раз.

Этот шаблон, в котором каждый вызов Builder возвращает ссылку на самого себя, позволяет объединить эти вызовы в цепочку, чтобы они были легко читаемыми и чтобы структура получаемого объекта была атомарной.

1 голос
/ 27 октября 2011

Вы определили Builder как вложенный класс NutritionFacts. Поскольку он статичен, для его существования не требуется экземпляр NutritionFacts. Если бы он не был статичным (так называемый «внутренний класс»), для его существования требовался бы NutritionFacts. Кроме того, некоторые из ваших полей Builder будут скрывать определенные поля NutritionFact, но сейчас это не так.

Теперь, так как вы использовали этот вложенный класс thingie, вы не можете просто назвать его как Builder. Вам придется называть это NutritionFacts.Builder. Таким образом, когда вы извлекаете второй код new NutritionFacts.Builder(240, 8), вы получаете новый экземпляр Builder с размером обслуживания 240 и 8 порций. NutritionFacts еще не вступил в игру, он существует только для названия.

Этот вновь создаваемый экземпляр может быть назначен некоторой переменной или может использоваться немедленно, скажем, путем вызова некоторого метода для него. Это то, что вы делаете, а именно вы называете .calories(100) на нем, который устанавливает поле калорий этого Строителя. Но если вы пойдете и посмотрите на этот метод, вы заметите, что он имеет тип возвращаемого значения Builder, и в итоге он возвращает this. Ключевое слово this просто ссылается на текущий экземпляр: тот же самый Builder снова.

То же самое происходит для .sodium(35) и .carbohydrate(27), после чего вы в конечном итоге вызываете .build() на своем Builder. Посмотри на этот метод. Он вызывает конструктор NutritionFacts. Этот конструктор принимает экземпляр Builder в качестве аргумента, и наш Builder передает себя (снова с this). Теперь мы наконец получаем экземпляр NutritionFacts, и он создается с использованием значений, которые были сохранены в экземпляре Builder.

Как сказал Джере, методы, которые устанавливают питательные вещества для Builder, используют подход цепочки методов для возврата объекта, к которому они были вызваны, так что несколько методов могут быть удобно объединены в одну строку. В действительности, ваш второй отрывок может быть написан так:

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