固定長文字列フォーマットからクラスオブジェクトを求める

Stream の reduceで文字列を長さで分割 - Oboe吹きプログラマの黙示録
を書いた時、応用すれば長いコードを書かなくても済むのでは?と思いました。
解析対象の固定長フォーマットの文字列、長さ 4,2,6,14 の文字列長の連結

String str = "uranA1   98120230312114807";

これを以下、文字列を解析して取得するJava Object のクラスになるとする。

import java.time.LocalDateTime;

import lombok.Data;
@Data
public class Foo {
    private String name;
    private String rank;
    private int value;
    private LocalDateTime atime;
}

文字列String を substring で取得する Function<T, R> と成果物に対する BiConsumer<T, U> が
成立すれば良いのです。

以前、yipuan-core ライブラリとして、ApplyConsumer という名の関数型インターフェース
https://github.com/yipuran/yipuran-core/wiki#applyconsumert-u
を作っています。
ソースは、、、
https://github.com/yipuran/yipuran-core/blob/master/src/main/java/org/yipuran/function/ApplyConsumer.java

これを使って、以下、列挙型で解析対象のクラスへの変換定義として書きます。

public enum  FooConfig{
    NAME(1, 4, ApplyConsumer.of(s->s, Foo::setName)),
    RANK(2, 2, ApplyConsumer.of(s->s, Foo::setRank)),
    VALUE(3, 6, ApplyConsumer.of(s->Integer.parseInt(s.trim()), Foo::setValue)),
    ATIME(4, 14, ApplyConsumer.of(
s->LocalDateTime.parse(s, DateTimeFormatter.ofPattern("uuuuMMddHHmmss")
 .withResolverStyle(ResolverStyle.STRICT)), Foo::setAtime));

    private int order;
    private int len;
    private ApplyConsumer<String, Foo> ap;
    private FooConfig(int order, int len, ApplyConsumer<String, Foo> ap) {
        this.order = order;
        this.len = len;
        this.ap = ap;
    }    
    public ApplyConsumer<String, Foo> ApplyConsumer(){
        return ap;
    }
    public int getLen() {
        return len;
    }
    public Integer getOrder() {
        return order;
    }
    public static List<FooConfig> list(){
        return Arrays.stream(FooConfig.values())
                .sorted((a, b)->a.getOrder().compareTo(b.getOrder()))
                .collect(Collectors.toList());
    }
}

この列挙型は、
int order; で書式文字列の中の並びの順番を定義してます。
int len; で、各フィールドの文字列長を定義してます。
ApplyConsumer で指定する Function<T, R> も、ここで String の trim() を書いたり、
場合によっては、例外を発生させたり用途に応じた変換を書けるわけです。

使用例は、以下のように書けます。
FooConfig.list() で書式の並び順のFooConfigリストから、String#substring で分割した結果を
一時保存してから、ApplyConsumer を実行する

List<String> datalist = new ArrayList<>();
FooConfig.list().stream().map(e->e.getLen())
.reduce(0, (a, b)->{
    datalist.add(str.substring(a, (a+b)));
    return a+b;
});

Foo foo = FooConfig.list().stream().collect(Foo::new,(r, t)->{
    t.getApplyConsumer().accept(datalist.get(t.getOrder()-1), r);
},(r, t)->{});

String#substring で分割した結果の一時保存をしない場合は、以下のように
reduce の実行だけで済ませる。その代わり求めるオブジェクトはあらかじめ生成しておく。

Foo foo = new Foo();
List<FooConfig> configlist = FooConfig.list();
AtomicInteger count = new AtomicInteger(0);
configlist.stream().map(e->e.getLen())
.reduce(0, (a, b)->{
    FooConfig config = configlist.get(count.getAndIncrement());
    config.getApplyConsumer().accept(str.substring(a, (a+b)), foo);
    return a+b;
});

生成した Foo を標準出力すると、、lombok の toString() で

Foo(name=uran, rank=A1, value=981, atime=2023-03-12T11:48:07)

となります。