クラスがあります
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
を変更せずに)
これが繰り返し発生する場合は、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());
_
これがFoo
やsetName
メソッドに固有ではないことに注意してください。
_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(","))
を使用する方が効率的かもしれません。 。
いいえ、良い方法はありません。
あなたが質問で言ったように、唯一の選択肢は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(","))
を使用できます(そして、再作成を避けるためにコンパイルされたパターンを定数に格納します)。
この場合、名前をパラメーターとして取るコンストラクターを追加するか、インスタンスを作成する static factory method を作成しない限り、あまり多くの選択肢はありません。
.map(n -> new Foo() {{ name = n; }} )
これは、初期化ブロックを使用してインスタンス変数を設定します。
ただし、注意が必要です。返されるオブジェクトは、実際にはFoo
型ではなく、Foo
を拡張する新しい匿名クラスになります。 Liskov置換の原則に従う場合、これは問題になりませんが、懸念される状況がいくつかあります。
誰もまだ言及していない別の代替手段はFoo
クラスをサブクラスにすることですが、これにはいくつかの欠点があります-コンテキストがわからないので、問題の適切な解決策になるかどうかを言うのは困難です。
public class Bar extends Foo {
public Bar(String name) {
super.setName(name);
}
}