Javaの名前付きパラメータイディオム


81

Javaで名前付きパラメータのイディオムを実装する方法は?(特にコンストラクターの場合)

JavaBeansで使用されているものではなく、Objective-Cのような構文を探しています。

小さなコード例で十分です。

ありがとう。

回答:


105

コンストラクターでキーワード引数をシミュレートするために私が考えた中で最高のJavaイディオムは、Effective Java 2ndEditionで説明されているBuilderパターンです。

基本的な考え方は、さまざまなコンストラクターパラメーターのセッター(通常はゲッターではない)を持つBuilderクラスを用意することです。build()方法もあります。Builderクラスは、多くの場合、ビルドに使用されるクラスの(静的な)ネストされたクラスです。多くの場合、外部クラスのコンストラクターはプライベートです。

最終結果は次のようになります。

public class Foo {
  public static class Builder {
    public Foo build() {
      return new Foo(this);
    }

    public Builder setSize(int size) {
      this.size = size;
      return this;
    }

    public Builder setColor(Color color) {
      this.color = color;
      return this;
    }

    public Builder setName(String name) {
      this.name = name;
      return this;
    }

    // you can set defaults for these here
    private int size;
    private Color color;
    private String name;
  }

  public static Builder builder() {
      return new Builder();
  }

  private Foo(Builder builder) {
    size = builder.size;
    color = builder.color;
    name = builder.name;
  }

  private final int size;
  private final Color color;
  private final String name;

  // The rest of Foo goes here...
}

Fooのインスタンスを作成するには、次のように記述します。

Foo foo = Foo.builder()
    .setColor(red)
    .setName("Fred")
    .setSize(42)
    .build();

主な注意事項は次のとおりです。

  1. パターンの設定はかなり冗長です(ご覧のとおり)。多くの場所でインスタンス化する予定のクラスを除いて、おそらくそれだけの価値はありません。
  2. すべてのパラメーターが1回だけ指定されていることをコンパイル時にチェックすることはありません。ランタイムチェックを追加することも、これをオプションのパラメーターにのみ使用して、必要なパラメーターをFooまたはBuilderのコンストラクターの通常のパラメーターにすることもできます。(通常、同じパラメーターが複数回設定されている場合は心配しません。)

このブログ投稿(私ではない)もチェックしてみてください。


12
これは、Objective-Cのように名前が付けられたパラメーターではありません。それは流暢なインターフェースのように見えます。それは本当に同じことではありません。
asaph 2010年

30
:では.withFooなく、を使用するのが好きです.setFoonewBuilder().withSize(1).withName(1).build()newBuilder().setSize(1).setName(1).build()
notnoop 2010年

17
アサフ:はい、わかっています。Javaには名前付きパラメーターがありません。だから私はこれが「キーワード引数をシミュレートするために私が見た中で最高のJavaイディオム」だと言ったのです。Objective-Cの「名前付きパラメーター」も、特定の順序を強制するため、理想的とは言えません。LispやPythonのように真のキーワード引数ではありません。少なくともJavaBuilderパターンでは、実際のキーワード引数のように、順序ではなく名前を覚えておく必要があります。
ローレンスゴンサルベス2010年

14
notnoop:Builderの状態を変更するセッターであるため、「set」の方が好きです。はい、「with」は、すべてをチェーンしている単純な場合には見栄えがしますが、Builderを独自の変数に入れているより複雑な場合(おそらく条件付きでプロパティを設定しているため)、私はそのセットが好きですプレフィックスは、これらのメソッドが呼び出されたときにBuilderが変更されていることを完全に明確にします。「with」プレフィックスは私には機能しているように聞こえますが、これらのメソッドは明らかに機能していません。
ローレンスゴンサルベス2010年

4
There's no compile-time checking that all of the parameters have been specified exactly once.この問題は、それぞれがセッターまたはのいずれかをカバーBuilder1するBuilderN場所にインターフェースを戻すことで克服できますbuild()。コードの記述ははるかに複雑ですが、DSLのコンパイラサポートが付属しており、オートコンプリートの操作が非常に便利です。
rsp 2010年

73

これは言及する価値があります:

Foo foo = new Foo() {{
    color = red;
    name = "Fred";
    size = 42;
}};

いわゆるダブルブレース初期化子。これは実際には、インスタンス初期化子を持つ匿名クラスです。


26
興味深いテクニックですが、コードで使用するたびに新しいクラスが作成されるため、少し高価に思えます。
レッドハイエナ2010年

6
自動フォーマット、サブクラス化、およびシリアル化の警告は別として、これは実際にはプロパティベースの初期化のC#構文に非常に近いものです。ただし、4.0以降のC#にも名前付きパラメーターがあるため、後で足を撃たないようにするイディオムをシミュレートする必要があるJavaプログラマーとは異なり、プログラマーは選択に甘んじています。
Distortum 2012

3
これが可能であることを嬉しく思いますが、Red Hyenaが指摘したように、このソリューションは高価であるため、私は反対票を投じなければなりませんでした。JavaがPythonのように名前付きパラメーターを実際にサポートするまで待つことはできません。
ガットスター2013

12
賛成。これは、最も読みやすく簡潔な方法で質問に答えます。結構です。それは「パフォーマンスが悪い」です。ここで話しているミリ秒とビットはいくつですか?それらの1つ?数十?誤解しないでください-冗長なJavaナット(しゃれを意図した;)によって実行されるのは嫌なので、これは使用しません
2013年

2
完璧!パブリック/保護されたフィールドのみが必要です。これは間違いなく最良のソリューションであり、ビルダーよりもはるかに少ないオーバーヘッドを引き起こします。Hyena / Gattster:コメントを書く前に、(1)JLSを読み、(2)生成されたバイトコードを確認してください。
はJonatanKaźmierczak

21

こちらからアドバイスに従うこともできます:http//www.artima.com/weblogs/viewpost.jsp?thread = 118828

int value; int location; boolean overwrite;
doIt(value=13, location=47, overwrite=true);

通話サイトでは冗長ですが、全体的にオーバーヘッドが最小になります。


3
オーバーヘッドが低いのは良いことですが、それはとてもハックな感じがします。引数が多い場合は、おそらくBuilder()メソッドを使用します。
ガットスター2013

23
これは、名前付きパラメーターのポイントを完全に見逃していると思います。(これは、名前と値を関連付けるものがあります)。順番を逆にしても何の表示ありません。代わりにこれを行うの私は単純にコメントを追加するのでアドバイスします:doIt( /*value*/ 13, /*location*/ 47, /*overwrite*/ true )
Scheintod

20

Java 8スタイル:

public class Person {
    String name;
    int age;

    private Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    static PersonWaitingForName create() {
        return name -> age -> new Person(name, age);
    }

    static interface PersonWaitingForName {
        PersonWaitingForAge name(String name);
    }

    static interface PersonWaitingForAge {
        Person age(int age);
    }

    public static void main(String[] args) {

        Person charlotte = Person.create()
            .name("Charlotte")
            .age(25);

    }
}
  • 名前付きパラメーター
  • 引数の順序を修正する
  • 静的チェック->無名の人は不可能
  • 同じタイプの引数を誤って切り替えるのは難しい(望遠鏡コンストラクターで可能であるように)

3
いいね。引数の順序が可変ではないのはなんて残念なことでしょう。(しかし、私がこれを使用するとは言わないでください...)
Scheintod 2015

1
これは素晴らしいアイデアです。の定義はcreate()私のトラックで私を止めました。私はJavaでそのスタイルのラムダチェーンを見たことがありません。このアイデアをラムダを使用した別の言語で最初に発見しましたか?
kevinarpe 2018

2
それはカリー化と呼ばれます:en.wikipedia.org/wiki/Currying。ところで:それは賢いアイデアかもしれませんが、この名前付き引数のスタイルはお勧めしません。多くの引数を使用して実際のプロジェクトでテストしたところ、コードが読みにくく、ナビゲートしにくくなりました。
アレックス

最終的に、Javaにはパラメータという名前のVisualBasicスタイルが与えられます。C ++がそうではないので、Javaは以前はしませんでした。しかし、私たちは最終的にそこに着きます。Javaポリモーフィズムの90%は、オプションのパラメーターをハッキングしているだけだと思います。
タンタブル

7

Javaは、コンストラクターまたはメソッド引数のObjective-Cのような名前付きパラメーターをサポートしていません。さらに、これは実際にはJavaのやり方ではありません。Javaでは、典型的なパターンは、詳細に名前が付けられたクラスとメンバーです。クラスと変数は名詞である必要があり、指定されたメソッドは動詞である必要があります。創造性を発揮してJavaの命名規則から逸脱し、Objective-Cパラダイムをハックな方法でエミュレートできると思いますが、これは、コードの保守を担当する平均的なJava開発者には特に感謝されません。どの言語で作業する場合でも、特にチームで作業する場合は、その言語とコミュニティの慣習に固執する必要があります。


4
+ 1-現在使用している言語のイディオムに固執することについてのアドバイス。あなたのコードを読む必要がある他の人々のことを考えてください!
スティーブンC

3
私はあなたが良い点を言っていると思うのであなたの答えを賛成しました。なぜあなたが反対票を獲得したのかを推測しなければならなかったのなら、それはおそらくこれが質問に答えていないからでしょう。Q:「Javaで名前付きパラメーターを使用するにはどうすればよいですか?」A:「あなたはしません」
Gattster 2013

12
あなたの答えは質問とは何の関係もないと思うので、私は反対票を投じました。詳細な名前は、パラメーターの順序付けの問題を実際には解決しません。はい、名前でエンコードすることはできますが、それは明らかに実用的ではありません。無関係なパラダイムを持ち出すことは、1つのパラダイムがサポートされない理由を説明しません。
アンドレアスミューラー

7

Java 6を使用している場合は、変数パラメーターを使用し、静的をインポートして、はるかに優れた結果を生成できます。これの詳細はにあります:

http://zinzel.blogspot.com/2010/07/creating-methods-with-named-parameters.html

要するに、あなたは次のようなものを持つことができます:

go();
go(min(0));
go(min(0), max(100));
go(max(100), min(0));
go(prompt("Enter a value"), min(0), max(100));

2
私はそれが好きですが、それでも問題の半分しか解決しません。Javaでは、必要な値のコンパイル時チェックを失うことなく、誤って転置されたパラメーターを防ぐことはできません。
cdunn2001 2012年

型安全性がなければ、これは単純な//コメントよりも悪いです。
Peter Davis

7

このスタイルは、他の言語が持っているgetおよびsetプレフィックスなしで、名前付きパラメータープロパティ機能の両方に対応していることを指摘したいと思います。これはJavaの領域では従来型ではありませんが、特に他の言語を扱ったことがある場合は、より単純で理解しにくいものではありません。

public class Person {
   String name;
   int age;

   // name property
   // getter
   public String name() { return name; }

   // setter
   public Person name(String val)  { 
    name = val;
    return this;
   }

   // age property
   // getter
   public int age() { return age; }

   // setter
   public Person age(int val) {
     age = val;
     return this;
   }

   public static void main(String[] args) {

      // Addresses named parameter

      Person jacobi = new Person().name("Jacobi").age(3);

      // Addresses property style

      println(jacobi.name());
      println(jacobi.age());

      //...

      jacobi.name("Lemuel Jacobi");
      jacobi.age(4);

      println(jacobi.name());
      println(jacobi.age());
   }
}

6

どうですか

public class Tiger {
String myColor;
int    myLegs;

public Tiger color(String s)
{
    myColor = s;
    return this;
}

public Tiger legs(int i)
{
    myLegs = i;
    return this;
}
}

Tiger t = new Tiger().legs(4).color("striped");

5
build()のいくつかの制約を確認できるため、Builderの方がはるかに優れています。しかし、私はまた、セット/プレフィックスのない短い引数を好みます。
rkj 2012年

4
また、ビルドされたクラス(この場合はTiger)を不変にすることができるため、ビルダーパターンの方が優れています。
ジェフオルソン

2

引数に名前を付ける通常のコンストラクターと静的メソッドを使用できます。

public class Something {

    String name;
    int size; 
    float weight;

    public Something(String name, int size, float weight) {
        this.name = name;
        this.size = size;
        this.weight = weight;
    }

    public static String name(String name) { 
        return name; 
    }

    public static int size(int size) {
        return size;
    }

    public float weight(float weight) {
        return weight;
    }

}

使用法:

import static Something.*;

Something s = new Something(name("pen"), size(20), weight(8.2));

実際の名前付きパラメーターと比較した制限:

  • 引数の順序は適切です
  • 可変引数リストは、単一のコンストラクターでは使用できません
  • すべての引数にメソッドが必要です
  • コメントよりも本当に良いわけではありません(new Something(/*name*/ "pen", /*size*/ 20, /*weight*/ 8.2)

選択肢がある場合は、Scala2.8をご覧ください。http://www.scala-lang.org/node/2075


2
このアプローチの欠点の1つは、引数を正しい順序で取得する必要があることです。上記のコードを使用すると、次のように記述できます。Somethings = new Something(name( "pen")、size(20)、size(21)); また、このアプローチは、オプションの引数の入力を回避するのに役立ちません。
マットウズラ2010年

1
私はこれを分析にnot really better than a comment
賛成し

2

Java 8のラムダを使用すると、実際の名前付きパラメーターにさらに近づくことができます。

foo($ -> {$.foo = -10; $.bar = "hello"; $.array = new int[]{1, 2, 3, 4};});

これはおそらく数十の「Javaのベストプラクティス」に違反していることに注意してください($シンボルを使用するものと同様)。

public class Main {
  public static void main(String[] args) {
    // Usage
    foo($ -> {$.foo = -10; $.bar = "hello"; $.array = new int[]{1, 2, 3, 4};});
    // Compare to roughly "equivalent" python call
    // foo(foo = -10, bar = "hello", array = [1, 2, 3, 4])
  }

  // Your parameter holder
  public static class $foo {
    private $foo() {}

    public int foo = 2;
    public String bar = "test";
    public int[] array = new int[]{};
  }

  // Some boilerplate logic
  public static void foo(Consumer<$foo> c) {
    $foo foo = new $foo();
    c.accept(foo);
    foo_impl(foo);
  }

  // Method with named parameters
  private static void foo_impl($foo par) {
    // Do something with your parameters
    System.out.println("foo: " + par.foo + ", bar: " + par.bar + ", array: " + Arrays.toString(par.array));
  }
}

長所:

  • これまでに見たどのビルダーパターンよりもかなり短い
  • メソッドとコンストラクターの両方で機能します
  • 完全にタイプセーフ
  • 他のプログラミング言語の実際の名前付きパラメータに非常に近いように見えます
  • 通常のビルダーパターンとほぼ同じくらい安全です(パラメーターを複数回設定できます)

短所:

  • あなたの上司はおそらくこれのためにあなたをリンチするでしょう
  • 何が起こっているのか見分けるのは難しい

1
短所:フィールドは公開されており、最終的なものではありません。これでよければ、セッターだけを使ってみませんか?メソッドではどのように機能しますか?
アレックス

セッターを使用できますが、そのポイントは何ですか?コードが長くなるだけで、このようにするメリットがなくなります。割り当ては副作用がなく、セッターはブラックボックスです。$foo(誰かがコールバック内の変数に割り当てない限り)呼び出し元にエスケープすることは決してないので、なぜそれらをパブリックにできないのですか?
vic

2

プロジェクトLombokの@Builderアノテーションを使用して、Javaで名前付きパラメーターをシミュレートできます。これにより、任意のクラス(作成したクラスと外部ライブラリからのクラスの両方)の新しいインスタンスを作成するために使用できるビルダーが生成されます。

これは、クラスでそれを有効にする方法です。

@Getter
@Builder
public class User {
    private final Long id;
    private final String name;
}

その後、次の方法でこれを使用できます。

User userInstance = User.builder()
    .id(1L)
    .name("joe")
    .build();

ライブラリからのクラスに対してこのようなBuilderを作成する場合は、次のような注釈付きの静的メソッドを作成します。

class UserBuilder {
    @Builder(builderMethodName = "builder")
    public static LibraryUser newLibraryUser(Long id, String name) {
        return new LibraryUser(id, name);
    }
  }

これにより、次の方法で呼び出すことができる「builder」という名前のメソッドが生成されます。

LibraryUser user = UserBuilder.builder()
    .id(1L)
    .name("joe")
    .build();

Googleの自動/値は同様の目的を果たしますが、アノテーション処理フレームワークを使用します。これは、プロジェクトのLombocksバイトコード操作よりもはるかに安全です(JVMのアップグレード後も機能します)。
ルネ・

1
Googleの自動/値は、ロンボクを使用する場合と比較して、少し余分なマイルが必要だと思います。Lombokのアプローチは、従来のJavaBeanの記述と多かれ少なかれ互換性があります(たとえば、newを介してインスタンス化できる、フィールドがデバッガーに適切に表示されるなど)。私はまた、Lombokが使用するバイトコード操作+ IDEプラグインソリューションの大ファンではありませんが、実際には問題なく機能することを認めなければなりません。これまでのJDKのバージョン変更、反射などの問題なし
イシュトヴァーンDevai

はい、それは本当だ。自動/値の場合、実装される抽象クラスを提供する必要があります。Lombokははるかに少ないコードを必要とします。したがって、賛否両論を比較する必要があります。
ルネ・

2

「コメントの回避策」はそれ自体の答えに値するように感じます(既存の回答には隠されており、ここのコメントに記載されています)。

someMethod(/* width */ 1024, /* height */ 768);

1

これは、Builder上記のローレンスによって説明されたパターンの変形です。

私はこれを(適切な場所で)頻繁に使用していることに気づきます。

主な違いは、この場合、ビルダーは不変であるということです。これには、再利用が可能で、スレッドセーフであるという利点があります。

したがって、これを使用して1つのデフォルトビルダーを作成し、それを必要とするさまざまな場所で構成してオブジェクトをビルドできます。

これは、同じオブジェクトを何度もビルドする場合に最も理にかなっています。これは、ビルダーを静的にすることができ、設定の変更について心配する必要がないためです。

一方、パラメータを変更してオブジェクトを作成する必要がある場合は、オーバーヘッドが静かになります。(ただし、静的/動的生成をカスタムbuildメソッドと組み合わせることができます)

サンプルコードは次のとおりです。

public class Car {

    public enum Color { white, red, green, blue, black };

    private final String brand;
    private final String name;
    private final Color color;
    private final int speed;

    private Car( CarBuilder builder ){
        this.brand = builder.brand;
        this.color = builder.color;
        this.speed = builder.speed;
        this.name = builder.name;
    }

    public static CarBuilder with() {
        return DEFAULT;
    }

    private static final CarBuilder DEFAULT = new CarBuilder(
            null, null, Color.white, 130
    );

    public static class CarBuilder {

        final String brand;
        final String name;
        final Color color;
        final int speed;

        private CarBuilder( String brand, String name, Color color, int speed ) {
            this.brand = brand;
            this.name = name;
            this.color = color;
            this.speed = speed;
        }
        public CarBuilder brand( String newBrand ) {
            return new CarBuilder( newBrand, name, color, speed );
        }
        public CarBuilder name( String newName ) {
            return new CarBuilder( brand, newName, color, speed );
        }
        public CarBuilder color( Color newColor ) {
            return new CarBuilder( brand, name, newColor, speed );
        }
        public CarBuilder speed( int newSpeed ) {
            return new CarBuilder( brand, name, color, newSpeed );
        }
        public Car build() {
            return new Car( this );
        }
    }

    public static void main( String [] args ) {

        Car porsche = Car.with()
                .brand( "Porsche" )
                .name( "Carrera" )
                .color( Color.red )
                .speed( 270 )
                .build()
                ;

        // -- or with one default builder

        CarBuilder ASSEMBLY_LINE = Car.with()
                .brand( "Jeep" )
                .name( "Cherokee" )
                .color( Color.green )
                .speed( 180 )
                ;

        for( ;; ) ASSEMBLY_LINE.build();

        // -- or with custom default builder:

        CarBuilder MERCEDES = Car.with()
                .brand( "Mercedes" )
                .color( Color.black )
                ;

        Car c230 = MERCEDES.name( "C230" ).speed( 180 ).build(),
            clk = MERCEDES.name( "CLK" ).speed( 240 ).build();

    }
}

1

Javaのソリューションはどれもかなり冗長になる可能性がありますが、Google AutoValuesImmutablesなどのツールは、JDKコンパイル時アノテーション処理を使用してビルダークラスを自動的に生成することを言及する価値があります。

私の場合、名前付きパラメーターをJava列挙型で使用したかったので、列挙型インスタンスを他のクラスでインスタンス化できないため、ビルダーパターンは機能しませんでした。@deamonの答えに似たアプローチを思いつきましたが、パラメーターの順序のコンパイル時チェックを追加します(より多くのコードを犠牲にして)

クライアントコードは次のとおりです。

Person p = new Person( age(16), weight(100), heightInches(65) );

そして実装:

class Person {
  static class TypedContainer<T> {
    T val;
    TypedContainer(T val) { this.val = val; }
  }
  static Age age(int age) { return new Age(age); }
  static class Age extends TypedContainer<Integer> {
    Age(Integer age) { super(age); }
  }
  static Weight weight(int weight) { return new Weight(weight); }
  static class Weight extends TypedContainer<Integer> {
    Weight(Integer weight) { super(weight); }
  }
  static Height heightInches(int height) { return new Height(height); }
  static class Height extends TypedContainer<Integer> {
    Height(Integer height) { super(height); }
  }

  private final int age;
  private final int weight;
  private final int height;

  Person(Age age, Weight weight, Height height) {
    this.age = age.val;
    this.weight = weight.val;
    this.height = height.val;
  }
  public int getAge() { return age; }
  public int getWeight() { return weight; }
  public int getHeight() { return height; }
}

0

kargライブラリでサポートされているイディオムは、検討する価値があるかもしれません。

class Example {

    private static final Keyword<String> GREETING = Keyword.newKeyword();
    private static final Keyword<String> NAME = Keyword.newKeyword();

    public void greet(KeywordArgument...argArray) {
        KeywordArguments args = KeywordArguments.of(argArray);
        String greeting = GREETING.from(args, "Hello");
        String name = NAME.from(args, "World");
        System.out.println(String.format("%s, %s!", greeting, name));
    }

    public void sayHello() {
        greet();
    }

    public void sayGoodbye() {
        greet(GREETING.of("Goodbye");
    }

    public void campItUp() {
        greet(NAME.of("Sailor");
    }
}

これは基本的にR Casha答えと同じようですが、それを説明するコードがありません。
Scheintod 2015

0

これは、コンパイラーによってチェックされたBuilderパターンです。警告:

  • これは引数の二重代入を防ぐことはできません
  • あなたは良い.build()方法を持つことはできません
  • フィールドごとに1つの汎用パラメーター

したがって、渡されないと失敗するクラス外の何かが必要ですBuilder<Yes, Yes, Yes>getSum例として静的メソッドを参照してください。

class No {}
class Yes {}

class Builder<K1, K2, K3> {
  int arg1, arg2, arg3;

  Builder() {}

  static Builder<No, No, No> make() {
    return new Builder<No, No, No>();
  }

  @SuppressWarnings("unchecked")
  Builder<Yes, K2, K3> arg1(int val) {
    arg1 = val;
    return (Builder<Yes, K2, K3>) this;
  }

  @SuppressWarnings("unchecked")
  Builder<K1, Yes, K3> arg2(int val) {
    arg2 = val;
    return (Builder<K1, Yes, K3>) this;
  }

  @SuppressWarnings("unchecked")
  Builder<K1, K2, Yes> arg3(int val) {
    this.arg3 = val;
    return (Builder<K1, K2, Yes>) this;
  }

  static int getSum(Builder<Yes, Yes, Yes> build) {
    return build.arg1 + build.arg2 + build.arg3;
  }

  public static void main(String[] args) {
    // Compiles!
    int v1 = getSum(make().arg1(44).arg3(22).arg2(11));
    // Builder.java:40: error: incompatible types:
    // Builder<Yes,No,Yes> cannot be converted to Builder<Yes,Yes,Yes>
    int v2 = getSum(make().arg1(44).arg3(22));
    System.out.println("Got: " + v1 + " and " + v2);
  }
}

警告は説明しました。なぜビルドメソッドがないのですか?問題は、それがBuilderクラス内にありK1, K2, K3、などでパラメーター化されることです。メソッド自体はコンパイルする必要があるため、呼び出すものはすべてコンパイルする必要があります。したがって、一般的に、クラス自体のメソッドにコンパイルテストを入れることはできません。

同様の理由で、ビルダーモデルを使用して二重代入を防ぐことはできません。


-1

@irreputableは素晴らしい解決策を思いついた。ただし、検証と整合性チェックが行われないため、Classインスタンスが無効な状態のままになる可能性があります。したがって、これをBuilderソリューションと組み合わせて、追加のサブクラスが作成されるのを避けたいと思いますが、それでもBuilderクラスはサブクラス化されます。さらに、追加のビルダークラスを使用すると冗長になるため、ラムダを使用してメソッドをもう1つ追加しました。完全を期すために、他のビルダーアプローチをいくつか追加しました。

次のようなクラスから始めます。

public class Foo {
  static public class Builder {
    public int size;
    public Color color;
    public String name;
    public Builder() { size = 0; color = Color.RED; name = null; }
    private Builder self() { return this; }

    public Builder size(int size) {this.size = size; return self();}
    public Builder color(Color color) {this.color = color; return self();}
    public Builder name(String name) {this.name = name; return self();}

    public Foo build() {return new Foo(this);}
  }

  private final int size;
  private final Color color;
  private final String name;

  public Foo(Builder b) {
    this.size = b.size;
    this.color = b.color;
    this.name = b.name;
  }

  public Foo(java.util.function.Consumer<Builder> bc) {
    Builder b = new Builder();
    bc.accept(b);
    this.size = b.size;
    this.color = b.color;
    this.name = b.name;
  }

  static public Builder with() {
    return new Builder();
  }

  public int getSize() { return this.size; }
  public Color getColor() { return this.color; }  
  public String getName() { return this.name; }  

}

次に、これを使用してさまざまな方法を適用します。

Foo m1 = new Foo(
  new Foo.Builder ()
  .size(1)
  .color(BLUE)
  .name("Fred")
);

Foo m2 = new Foo.Builder()
  .size(1)
  .color(BLUE)
  .name("Fred")
  .build();

Foo m3 = Foo.with()
  .size(1)
  .color(BLUE)
  .name("Fred")
  .build();

Foo m4 = new Foo(
  new Foo.Builder() {{
    size = 1;
    color = BLUE;
    name = "Fred";
  }}
);

Foo m5 = new Foo(
  (b)->{
    b.size = 1;
    b.color = BLUE;
    b.name = "Fred";
  }
);

@LaurenceGonsalvesがすでに投稿したものから部分的に完全にぼったくりのように見えますが、選択した規則にわずかな違いがあります。

JLSが名前付きパラメーターを実装する場合、どのように実装するのでしょうか。短い形式のサポートを提供することで、既存のイディオムの1つを拡張するのでしょうか。また、Scalaは名前付きパラメーターをどのようにサポートしていますか?

うーん-研究するのに十分、そして多分新しい質問。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.