昨日投稿の、Java Bean のコピー(1)、 Snake case ⇔ Camel case - Oboe吹きプログラマの黙示録
は、コピー先に Camele ⇔ Snale 対応の同じ型フィールドが存在しなくても属性値コピー生成する方法だ。
でも、厳しく、機能上より優れてる以下の属性値コピー生成を求めるなら、
・ コピー先に必須で、 Camele ⇔ Snale 変換名のフィールドが存在する。
・ コピー先に、コピー元に存在する属性が存在してコピー前に予めセットすることが可能(初期化コンストラクタで)
そこで、以下のように staticメソッドする。昨日投稿よりも処理が速いはずだ。
import java.util.function.Supplier; import java.util.Arrays; import java.util.regex.Pattern; import org.yipuran.function.ThrowableBiConsumer; import org.yipuran.function.ThrowableConsumer; import org.yipuran.util.Fieldsetter; import org.yipuran.util.GenericBuilder;
public static <R, T> R copySnakeToCamel(T t, Supplier<R> sup){ GenericBuilder<R> builder = Arrays.stream(t.getClass().getDeclaredFields()) .collect(ThrowableSupplier.to(()->GenericBuilder.of(sup)) , ThrowableBiConsumer.of((r, f)->{ f.setAccessible(true); String camel = Optional.of(Pattern.compile("_") .splitAsStream(f.getName().toLowerCase()).filter(e->e.length() > 0) .collect(StringBuilder::new, (rr, tt)->rr.append(tt.substring(0, 1).toUpperCase()).append(tt.substring(1)) , (v, w)->{}).toString()).filter(e->e.length() > 1) .map(e->e.substring(0, 1).toLowerCase() + e.substring(1)).get(); r.with(Fieldsetter.of((p, u)->camel), f.get(t)); }), (r, u)->{}); return builder.build(); } public static <R, T> R copyCamelToSnake(T t, Supplier<R> sup){ GenericBuilder<R> builder = Arrays.stream(t.getClass().getDeclaredFields()) .collect(ThrowableSupplier.to(()->GenericBuilder.of(sup)) , ThrowableBiConsumer.of((r, f)->{ f.setAccessible(true); String snake = f.getName().replaceAll("([a-z0-9]+)([A-Z]+)", "$1_$2").toLowerCase(); r.with(Fieldsetter.of((p, u)->snake), f.get(t)); }), (r, s)->{}); return builder.build(); }
このように、static メソッド、ユーティリティを用意するのではなく、
Java インターフェースの default メソッドで実装して、
コピー元のクラスで、インターフェース実装(implements)させる手段もあるだろう。
public interface SnakeCamel{ default <R> R toSnake(Supplier<R> sup){ GenericBuilder<R> builder = Arrays.stream(this.getClass().getDeclaredFields()) .collect(ThrowableSupplier.to(()->GenericBuilder.of(sup)) , ThrowableBiConsumer.of((r, f)->{ f.setAccessible(true); String snake = f.getName().replaceAll("([a-z0-9]+)([A-Z]+)", "$1_$2").toLowerCase(); r.with(Fieldsetter.of((p, u)->snake), f.get(this)); }), (r, s)->{}); return builder.build(); } default <R> R toCamel(Supplier<R> sup){ GenericBuilder<R> builder = Arrays.stream(this.getClass().getDeclaredFields()) .collect(ThrowableSupplier.to(()->GenericBuilder.of(sup)) , ThrowableBiConsumer.of((r, f)->{ f.setAccessible(true); String camel = Optional.of(Pattern.compile("_") .splitAsStream(f.getName().toLowerCase()).filter(e->e.length() > 0) .collect(StringBuilder::new , (rr, tt)->rr.append(tt.substring(0, 1).toUpperCase()).append(tt.substring(1)) , (v, w)->{}).toString()).filter(e->e.length() > 1) .map(e->e.substring(0, 1).toLowerCase() + e.substring(1)).get(); r.with(Fieldsetter.of((p, u)->camel), f.get(this)); }), (r, u)->{}); return builder.build(); } }
(使用例)
FooCamel ・・・属性名が Camel case で記述されたクラス
FooSnake ・・・属性名が Snale case で記述されたクラス
コピー元のクラスは、SnakeCamel を実装している。
public class FooSnake implements SnakeCamel
で、
Snake case → Camel case
FooSnake fs; // インスタンスが生成されてフィールド値が設定される処理の後、 FooCamel fc = fs.toCamel(FooCamel::new);
あるいは、
FooCamel fc = fs.toCamel(()->{ FooCamel f = new FooCamel(); // FooSnake に存在しない属性設定などを実行 return f; });
コピー元のクラスは、SnakeCamel を実装している。
public class FooCamel implements SnakeCamel
で、
Camel case → Snake case
FooCamel fc; // インスタンスが生成されてフィールド値が設定される処理の後、 FooSnake fs = fc.toSnake(FooSnake::new);
あるいは、
FooSnake fs = fc.toSnake(()->{ FooSnake f = new FooSnake(); // FooCamel に存在しない属性設定などを実行 return f; });