web-dev-qa-db-ja.com

Java 8-stream.map()でのコンストラクター呼び出しとセッターのチェーン

クラスがあります

class Foo{
    String name;
    // setter, getter
}

デフォルトのコンストラクタのみがあります。

次に、いくつかの文字列からFooのリストを作成しようとしています。

Arrays.stream(fooString.split(","))
            .map(name -> {
                Foo x = new Foo();
                x.setName(name);
                return x;

            }).collect(Collectors.toList()));

名前を取るコンストラクターがないため、単純にメソッド参照を使用することはできません。もちろん、コンストラクター呼び出しとセッターを使用してこれらの3行をメソッドに抽出することもできますが、それを行うより良いまたは簡潔な方法はありますか? (生成ファイルであるFooを変更せずに)

31
user140547

これが繰り返し発生する場合は、1つのプロパティ値を指定してオブジェクトを構築する問題を処理する汎用ユーティリティメソッドを作成できます。

_public static <T,V> Function<V,T> create(
    Supplier<? extends T> constructor, BiConsumer<? super T, ? super V> setter) {
    return v -> {
        T t=constructor.get();
        setter.accept(t, v);
        return t;
    };
}
_

その後、次のように使用できます。

_List<Foo> l = Arrays.stream(fooString.split(","))
    .map(create(Foo::new, Foo::setName)).collect(Collectors.toList());
_

これがFoosetNameメソッドに固有ではないことに注意してください。

_List<List<String>> l = Arrays.stream(fooString.split(","))
    .map(create(ArrayList<String>::new, List::add)).collect(Collectors.toList());
_

ところで、fooStringが非常に大きくなり、および/または(分割後)多数の要素を含む場合、Pattern.compile(",").splitAsStream(fooString)の代わりにArrays.stream(fooString.split(","))を使用する方が効率的かもしれません。 。

31
Holger

いいえ、良い方法はありません。

あなたが質問で言ったように、唯一の選択肢はFooオブジェクトのファクトリを作成することです:

_public class FooFactory {
    public static Foo fromName(String name) {
        Foo foo = new Foo();
        foo.setName(name);
        return foo;
    }
}
_

次のように使用します:

_Arrays.stream(fooString.split(",")).map(FooFactory::fromName).collect(toList());
_

分割する名前がたくさんある場合は、Pattern.compile(",").splitAsStream(fooString)の代わりにArrays.stream(fooString.split(","))を使用できます(そして、再作成を避けるためにコンパイルされたパターンを定数に格納します)。

11
Tunaki

この場合、名前をパラメーターとして取るコンストラクターを追加するか、インスタンスを作成する static factory method を作成しない限り、あまり多くの選択肢はありません。

8
aleroot

.map(n -> new Foo() {{ name = n; }} )

これは、初期化ブロックを使用してインスタンス変数を設定します。

ただし、注意が必要です。返されるオブジェクトは、実際にはFoo型ではなく、Fooを拡張する新しい匿名クラスになります。 Liskov置換の原則に従う場合、これは問題になりませんが、懸念される状況がいくつかあります。

4
Philipp

誰もまだ言及していない別の代替手段はFooクラスをサブクラスにすることですが、これにはいくつかの欠点があります-コンテキストがわからないので、問題の適切な解決策になるかどうかを言うのは困難です。

public class Bar extends Foo {

    public Bar(String name) {
        super.setName(name);
    }

}
3
Jaroslaw Pawlak