Java Bean のコピー(2)、 Snake case ⇔ Camel case

昨日投稿の、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;
});