LoginSignup
5
4

More than 5 years have passed since last update.

【Java】 JavaBeansのメソッドチェーンでの扱いについて

Last updated at Posted at 2016-12-11

JavaBeansで作成されたオブジェクトをメソッドチェーンでどう扱うかについて考えました。

JavaBeansの仕様

正確な仕様かどうかはわかりませんが、よくあるJavaBeansの仕様は以下のようなものですね。

  • 引き数なしのコンストラクタを持つ
  • プロパティを持つ(setter/getter)

ほかにもいろいろありますが、大事なのはこんなもん。
公式の仕様はこちら→JavaBeans Spec

HogeBean.java
public class HogeBean {

    private String name;

    private String id;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }
}

こんな感じ。lombokを使えば

HogeBean.java
@Data
public class HogeBean {

    private String name;

    private String id;
}

これでOK。

メソッドチェーンでの問題点

例えば似たようなフィールドを持つHogeBeanとFugaBeanがあるとする。

HogeBean.java
@Data
public class HogeBean {

    private String name;

    private String id;
}
FugaBean.java
@Data
public class FugaBean {

    private String id;

    private String password;
}

Stream中でHogeBeanをFugaBeanに変換したい場合、普通に考えると

List<HogeBean> hoges = ...

hoges.stream().map(hoge -> {

    FugaBean fuga = new FugaBean();
    fuga.setId(hoge.getId());
    return fuga;

}).終端処理

こんな感じでしか書くことになる。こんなの美しくない。

解決策1 匿名クラス + インスタンスイニシャライザ

あまり美しいとは言えないかもしれませんが、map内の中括弧 + return は防げるパターン。

hoges.stream().map(hoge -> new FugaBean(){
    {
        setId(hoge.getId());
    }
}).終端処理

さっきのよりはだいぶスッキリ。
ただ欠点があって、

  • 匿名クラスを使うため、厳密にはそのクラスではない。→ リフレクションとかでバグる可能性がある
  • そもそも対象のクラスがfinalだと使えない

解決策2 setterで自身を返すようにして、1行でオブジェクト生成可能なようにする。

JavaBeansをやめるという選択肢。
setterを以下のように改造する。

FugaBean.java
    public HogeBean setId(String id) {
        this.id = id;
        return this;
    }

lombokならAccessors(chain = true)を付けるだけ

FugaBean.java
@Data
@Accessors(chain = true)

件の処理は下記のように出来て

hoges.stream().map(hoge -> new FugaBean().setId(hoge.getId())).終端処理

スッキリ。
でもやっぱり欠点はあって、

  • JavaBeans仕様から外れるため、いろんなものが使えなくなる(PropertyDescriptor, apacheのBeanUtils, フレームワーク各種)

解決策3 ちょっとしたBuilderを作る

こんなん

BeanCreater.java
public final class BeanCreater<T> {

    private BeanCreater() {
    }

    private T bean;

    public static <T> BeanCreater<T> of(Supplier<T> supplier) {

        BeanCreater<T> builder = new BeanCreater<T>();
        builder.bean = supplier.get();

        return builder;

    }

    public BeanCreater<T> construct(Consumer<T> consumer) {

        consumer.accept(this.bean);
        return this;
    }

    public T build() {
        return this.bean;
    }

}

これを使えば、

hoges.stream().map(hoge -> BeanCreater.of(FugaBean::new)
    .construct(bean -> bean.setId(hoge.getId())).build()).終端処理

うーん。やってみたものの、いまいち。コードにノイズが多すぎますね。

結論

個人的には解決策2が一番好きです。流れるようなインタフェースをかけるし、本質と儀式的な話でも無駄がない。
JavaBeansが現状を考えてもっと柔軟になってほしいなあと思います。(そんな訳にもいかないんでしょうが... )
今回作成したBeanCreaterはこちら https://github.com/7tsuno/BeanCreater

何か他に案があればアドバイスください!

5
4
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4