任意オブジェクト生成→属性設定済みを取得するのに、簡潔を目指していた。
例)
import lombok.Data; @Data public class Member{ private String memberId; private String firstName; private String lastName; private int limitDays; public void setName(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } }
この Member インスタンスを作って属性をセットするのに
Member member = new Member(); // Member setter 実行 member.setMemberId(1);
こういう記述を書くことを避けたくて
BiConsumer と設定したい値を与えて生成するビルダを作った。
⇒ GenericBuilder
https://github.com/yipuran/yipuran-core/blob/master/src/main/java/org/yipuran/util/GenericBuilder.java
これを使って
Member member = GenericBuilder.of(Member::new) .with(Member::setMemberId, "A1201") .with(Member::setFirstName, "太郎") .with(Member::setLastName, "山田") .with(Member::setLimitDays, 60) .build();
GenericBuilder は、使い回しできる。
似たようなロジックを実行するものとして考えたのが、
BiConsumer として accept 実行した結果を Function として返すだけのインターフェース
https://github.com/yipuran/yipuran-core/blob/master/src/main/java/org/yipuran/function/AcceptFunction.java
これを java.util.Optional で使うことで
Member member = Optional.of(new Member()) .map(AcceptFunction.of(Member::setMemberId, "A1201")) .map(AcceptFunction.of(Member::setFirstName, "太郎")) .map(AcceptFunction.of(Member::setLastName, "山田")) .map(AcceptFunction.of(Member::setLimitDays, 60)) .get();
GenericBuilder と違ってこれは使い回しもなにもない。
これら2つの方法には欠点がある。BiConsumer でsetterメソッドの指定をするから
当然、引数1個のsetter メソッドしか指定できない。
上にあげた Memberクラスのように、
public void setName(String firstName, String lastName)
メソッドを指定することができない。
そこで最初に考えたのは、accept結果を取得するようにする java.util.function.UnaryOperator です。
次のメソッドを用意
public static <T> UnaryOperator<T> nextAccept(Consumer<T> con){ return e->{ con.accept(e); return e; }; }
UnaryOperator を1つ1つ書くと、、
UnaryOperator<Member> u1 = nextAccept(e->e.setMemberId("A1210")); UnaryOperator<Member> u2 = nextAccept(e->e.setName("太郎", "山田")); UnaryOperator<Member> u3 = nextAccept(e->e.setLimitDays(60)); Member member = u1.andThen(u2).andThen(u3).apply(new Member());
UnaryOperator の andThen で連結すると、
UnaryOperator<Member> uop = nextAccept(e->e.setMemberId("A1210")); Member member = uop.andThen(nextAccept(e->e.setName("太郎", "山田"))) .andThen(nextAccept(e->e.setLimitDays(60))) .apply(new Member());
生成する UnaryOperator という点で使い回しできるかもしれないが、staticメソッドであって
使用に抵抗がある。
BiConsumer ではなく Consumer 実行で考えたのが以下の方法で、
総称型クラスの型をどうにかして認識させる必要があることでした。
以下のとおり of メソッドで対象クラスを指定してしまった方が、Consumerコーディング時に縛りも生まれます。
import java.io.Serializable; import java.util.function.Consumer; @FunctionalInterface public interface ReturnalConsumer<T> extends Serializable{ T apply(T t); public static <T> ReturnalConsumer<T> of(Class<T> t){ return new ReturnalConsumer<T>(){ @Override public T apply(T u){ return u; } }; } default ReturnalConsumer<T> with(Consumer<T> c){ return t ->{ c.accept(apply(t)); return t; }; } default T get(T t) { return apply(t); } }
これは、ここに入れました。
https://github.com/yipuran/yipuran-core/blob/master/src/main/java/org/yipuran/function/ReturnalConsumer.java
これなら、上の GenericBuilder のように、BiConsumer で縛られることなく
以下のように、2つの引数メソッド、setName(String firstName, String lastName) も実行できます。
Member member = ReturnalConsumer.of(Member.class) .with(e->e.setMemberId("A1210")) .with(e->e.setName("太郎", "山田")) .with(e->e.setLimitDays(60)) .get(new Member());
もちろん、ReturnalConsumer 使い回しもできて、
Optional で以下のように使えます。
ReturnalConsumer<Member> rc = ReturnalConsumer.of(Member.class) .with(e->e.setMemberId("A1210")) .with(e->e.setName("太郎", "山田")) .with(e->e.setLimitDays(60)); Member member = Optional.of(new Member()).map(e->rc.get(e)).get();
あくまでも Consumer を実行した結果を取得するだけだから、
GenericBuilder で生成、→ 2つ以上の引数の実行メソッドがあったら、
ReturnalConsumer を使用というように、
使い分けしても良いはずだ。