ドメインクラスで型パラメータをサポート

twitter:@backpaper0
さんの発言から。



いいですね!

さっそく実装してSNAPSHOTつくってみました。


ぜひお試しください。ブラッシュアップして次のバージョン(1.32.0)で正式リリースに組み込みたいと考えています。

使用例


使用例を示します。


以下のようにドメインクラスに任意の数の型パラメータを指定できます。ここでは1個だけですが、何個でもOK。

@Domain(valueType = int.class)
public class Weight<T> {

    private final int value;

    public Weight(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }

    public Weight<T> add(Weight<T> other) {
        return new Weight<T>(value + other.value);
    }
}


利用する側では次のように実型引数に具体的な型を指定します(ワイルドカードや型変数の使用はサポートしていません)。

@Entity
public class Person {

    public Weight<kg> weight;
}
@Entity
public class Food {

    public Weight<g> weight;
}


上の例では、単位を表すkgやgという型を作って型引数にしました。

public interface kg {}
public interface g {}


これでいったい何がうれしいかというと、WeightとWeightは別の型なので安全性を高められるということです。
つまり、次のようなコードはコンパイルできません。

Person person = ...
Food food = ...
Weight<kg> weight = person.weight.add(food.weight);

Domainでファクトリメソッドを使うには ?


上の使用例では、Domainクラスをコンストラクタでインスタンス化しています。
ファクトリメソッドが好きな人もいるでしょう。
ファクトリメソッドでは、クラスの型変数宣言と同等の宣言をしてください。
この例では、ofメソッドがファクトリメソッドです。

@Domain(valueType = int.class, factoryMethod = "of")
public class Weight<T> {

    private final int value;

    private Weight(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }

    public Weight<T> add(Weight<T> other) {
        return new Weight<T>(value + other.value);
    }

    public static <T> Weight<T> of(int value) {
        return new Weight<T>(value);
    }
}

ExternalDomainを使うには ?


ValueObjectにアノテーションをつけたくない(つけられない)場合のために、ExternalDomainという機能がありますが、こちらでも型パラメータに対応しています。
注意点は1つです。

  • DomainConverterの実装では、Domainクラスはワイルドカードを使って扱ってください。


これがValueObject。上の例と違ってアノテーションついていません。

public class Weight<T> {

    private final int value;

    public Weight(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }

    public Weight<T> add(Weight<T> other) {
        return new Weight<T>(value + other.value);
    }
}


これが、ValueObjectと基本的な値を相互変換するコンバーター(DomainConverterの実装)。Weightの型引数に?(ワイルドカード)を指定しています。

@ExternalDomain
public class WeightConverter implements DomainConverter<Weight<?>, Integer> {

    @Override
    public Integer fromDomainToValue(Weight<?> domain) {
        ...
    }

    @Override
    public Weight<?> fromValueToDomain(Integer value) {
        ...
    }
}