2016年6月12日日曜日

AutoValue ライブラリを試してみた

https://github.com/google/auto/blob/master/value/userguide/index.md

immutable value class を生成してくれるライブラリ。abstract クラスを用意して @AutoValue をつけると、equals() や hashCode() などの boilerplate なコードを実装したクラスを用意してくれる。

設定

dependencies { compile 'com.google.auto.value:auto-value:1.2' apt 'com.google.auto.value:auto-value:1.2' }

使い方

例えば @AutoValue abstract class Animal { abstract String name(); abstract int numberOfLegs(); } のようなクラスを定義すると、AutoValue_Animalというクラスが生成される。 final class AutoValue_Animal extends Animal { private final String name; private final int numberOfLegs; AutoValue_Animal( String name, int numberOfLegs) { if (name == null) { throw new NullPointerException("Null name"); } this.name = name; this.numberOfLegs = numberOfLegs; } @Override String name() { return name; } @Override int numberOfLegs() { return numberOfLegs; } @Override public String toString() { return "Animal{" + "name=" + name + ", " + "numberOfLegs=" + numberOfLegs + "}"; } @Override public boolean equals(Object o) { if (o == this) { return true; } if (o instanceof Animal) { Animal that = (Animal) o; return (this.name.equals(that.name())) && (this.numberOfLegs == that.numberOfLegs()); } return false; } @Override public int hashCode() { int h = 1; h *= 1000003; h ^= this.name.hashCode(); h *= 1000003; h ^= this.numberOfLegs; return h; } } コンスタクタでは name と numberOfLegs を引数に取り、equals() や hasCode() ではこれらを使った実装になっている。 immutable value class なのでコンスタクタで受け取った引数は final として保持される。

abstract クラスに static な生成メソッドを用意して利用する。 @AutoValue abstract class Animal { static Animal create(String name, int numberOfLegs) { return new AutoValue_Animal(name, numberOfLegs); } abstract String name(); abstract int numberOfLegs(); } コンスタクタの引数は null チェックされる。これを止めたいときは abstract メソッドの戻り値に @Nullable をつける。 @AutoValue abstract class Animal { @Nullable abstract String name(); abstract int numberOfLegs(); } final class AutoValue_Animal extends Animal { private final String name; private final int numberOfLegs; AutoValue_Animal( @Nullable String name, int numberOfLegs) { this.name = name; this.numberOfLegs = numberOfLegs; } ... } Builder を用意することも可能。@AutoValue をつけるクラスにインナークラスとして abstract static な Builder クラスを定義し、@AutoValue.Builder をつける。 @AutoValue abstract class Animal { abstract String name(); abstract int numberOfLegs(); static Builder builder() { return new AutoValue_Animal.Builder(); } @AutoValue.Builder abstract static class Builder { abstract Builder name(String value); abstract Builder numberOfLegs(int value); abstract Animal build(); } } final class AutoValue_Animal extends Animal { ... static final class Builder extends Animal.Builder { private String name; private Integer numberOfLegs; Builder() { } Builder(Animal source) { this.name = source.name(); this.numberOfLegs = source.numberOfLegs(); } @Override public Animal.Builder name(String name) { this.name = name; return this; } @Override public Animal.Builder numberOfLegs(int numberOfLegs) { this.numberOfLegs = numberOfLegs; return this; } @Override public Animal build() { String missing = ""; if (name == null) { missing += " name"; } if (numberOfLegs == null) { missing += " numberOfLegs"; } if (!missing.isEmpty()) { throw new IllegalStateException("Missing required properties:" + missing); } return new AutoValue_Animal( this.name, this.numberOfLegs); } } }