Цепной вызов в Java 7? - PullRequest
6 голосов
/ 18 июня 2009

Я только что прочитал предварительный просмотр Java 7 (pdf) , и там был слайд на Цепной вызов . Вот пример, используемый на слайде:

// Construction with setters
DrinkBuilder margarita = new DrinkBuilder();
margarita.add("tequila");
margarita.add("orange liqueur");
margarita.add("lime juice");
margarita.withRocks();
margarita.withSalt();
Drink drink = margarita.drink();

// Construction with chained invocation
Drink margarita = new DrinkBuilder()
    .add("tequila")
    .add("orange liqueur")
    .add("lime juice")
    .withRocks()
    .withSalt()
    .drink();

И у меня смешанные чувства по этому поводу. Не следует объединять слишком много вызовов методов в одно утверждение. С другой стороны, писать margarita.this() и margarita.that() тоже не слишком удобно.

Теперь я прихожу на Яву из мира Delphi. А в Delphi есть языковая конструкция with. Это лелеют немногие и ненавидят многие (или наоборот?). Я нахожу with более элегантным, чем идея цепного вызова (который, я считаю, работает на основе метода void, возвращающего ссылку на объект, для которого он был вызван - и эта часть мне не нравится, как void должен вернуть ничего ).

Буду признателен за поддержку языка with, принятую в Java, поэтому пример кода можно написать так:

Drink margarita = null;
with (new DrinkBuilder()) {
    add("tequila");
    add("orange liqueur");
    add("lime juice");
    withRocks();
    withSalt();
    margarita = drink();
}

Я единственный, кто предпочел бы это решение цепочному вызову? Кто-нибудь еще считает, что with может быть полезным расширением языка Java? (Напоминает мне о чьем-то вопросе о необходимости "Java ++" ...)

Ответы [ 6 ]

13 голосов
/ 18 июня 2009

оператор с может быть переведен на Java с использованием анонимных классов с инициализатором:

Drink margarita = new DrinkBuilder() {{
    add(“tequila”);
    add(“orange liqueur”);
    add(“lime juice”);
    withRocks();
    withSalt();
}}.drink();

недостатки использования этой идиомы хорошо документированы здесь .

Цепной вызов - это псевдоним для метода Цепочка. Это хорошо известная идиома, которая работает с любой версией Java:

class Chained {

    public Chained withFoo() { 
        // ...
        return this;
    }

    public Chained withBar() { 
        // ...
        return this;
    }
}    

предложение для JDK 7 - , допускающее метод цепочки также для типа пустого возврата :

class ChainedJava7 {

    public void withFoo() { 
        // ...
    }

    public void withBar() { 
        // ...
    }
}    
2 голосов
/ 18 июня 2009

Мне очень нравятся with операторы этой формы, но я предпочитаю их версию на VB:

With testObject
    .Height = 100
    .Text = "Hello, World"
    .ForeColor = System.Drawing.Color.Green
End With

Поскольку каждому атрибуту в блоке With все еще должен предшествовать ., вы знаете, что ссылаетесь на свойство Object, а не, скажем, на локальную переменную, уменьшая любые коллизии пространства имен.

Если мы возьмем ваш пример:

with (new DrinkBuilder()) {
    add(“tequila”);
    add(“orange liqueur”);
    add(“lime juice”);
    withRocks();
    withSalt();
    margarita = drink();
}

Нет простого способа определить, является ли withSalt() методом DrinkBuilder или методом в локальном классе. Если вы разрешите только методы объекта with в блоке with, то я думаю, что они станут гораздо менее полезными.

2 голосов
/ 18 июня 2009

Это может вас заинтересовать.

1 голос
/ 18 июня 2009

Джошуа Блох в Эффективная Java Элемент № 2 настоятельно рекомендует использовать Builder, когда у вас есть конструктор с большим количеством аргументов. Одна из причин заключается в том, что он может быть написан так, чтобы гарантировать, что построенный объект всегда находится в согласованном состоянии. Это также позволяет избежать сложных «телескопических конструкторов» в классе встроенного объекта. Еще одна проблема заключается в том, что если вы хотите, чтобы встроенный объект был неизменным (например, для обеспечения безопасности потоков), он не может иметь методов установки.

1 голос
/ 18 июня 2009

Может быть, множество вызовов одного объекта является признаком того, что некоторый код необходимо перемещать?

1 голос
/ 18 июня 2009

Я не фанат этого использования with; Я очень предпочитаю Python with оператор . Я согласен с вами, что void должно означать void, хотя. В приведенном вами примере, если человек действительно хочет иметь возможность связывать вызовы методов, он должен просто изменить типы возвращаемых значений в своих методах, чтобы они были цепными.

...