Java データエンティティなど、データを表現するインスタンス生成する時に威力を発揮する
ものを作っていた。。これを強化しようと思う。
(自分はエンティティという単語、DBに限定してしまうような表現が嫌い)
https://github.com/yipuran/yipuran-core/blob/master/src/main/java/org/yipuran/util/GenericBuilder.java
これは生成対象のクラスが、setter を持っていることが条件で with メソッドで
public <U> GenericBuilder<T> with(BiConsumer<T, U> consumer, U value)
により、setter を with の引数として BiConsumer に渡すことができた。
setter メソッドさえあれば、、、
メソッド参照、Object::setXxxx を BiConsumer とできたので、この with を簡潔に書けた。
public class Foo{ public int value; public void setValue(int value){ this.value = value; } } ///////// GenericBuilder.of(Foo::new).with(Foo::setValue, 16).build();
のように書けた。
しかし、setter を持っていないクラスでフィールドに値をセットするには
Throwable な BiConsumer
(自作 → https://github.com/yipuran/yipuran-core/blob/master/src/main/java/org/yipuran/function/ThrowableBiConsumer.java )
を使って、以下のようにしないとならない。
(例)Foo というクラス
public class Foo{ public int value; }
Foo の value に int 値 16 をセットして生成
Foo foo = GenericBuilder.of(Foo::new) .with(ThrowableBiConsumer.of((t, u)->t.getClass().getDeclaredField("value").set(t, u)), 16) .build();
では、対象フィールドが、public ではなく、private では以下のように長くなってしまう。
Foo foo = GenericBuilder.of(Foo::new) .with(ThrowableBiConsumer.of((t, u)->{ Field f = t.getClass().getDeclaredField("value"); f.setAccessible(true); f.set(t, u); }), 16) .build();
これは、少し辛い。
新しい関数型インターフェースを定義する。
package org.yipuran.util; import java.io.Serializable; import java.lang.reflect.Field; import java.util.function.BiConsumer; /** * Fieldsetter */ @FunctionalInterface public interface Fieldsetter<T, U> extends Serializable{ String get(T t, U u) throws Exception; public static <T, U> BiConsumer<T, U> of(Fieldsetter<T, U> function){ return (t, u)->{ try{ Field f = t.getClass().getDeclaredField(function.get(t, u)); f.setAccessible(true); f.set(t, u); }catch(Throwable ex){ throw new RuntimeException(ex); } }; } }
これならば、
Foo foo = GenericBuilder.of(Foo::new) .with(Fieldsetter.of((t, u)->"value"), 16) ,build();
これで生成クラスが setter を持っていなくても、private フィールドでも
GenericBuilder でビルドできる。
しかしリフレクションが走る分の遅さはある。
この Fieldsetter.of 他にも役立ちそうな気がする。