2016年6月12日日曜日

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

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

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

設定

  1. dependencies {  
  2.     compile 'com.google.auto.value:auto-value:1.2'  
  3.     apt 'com.google.auto.value:auto-value:1.2'  
  4. }  

使い方

例えば
  1. @AutoValue  
  2. abstract class Animal {  
  3.   
  4.     abstract String name();  
  5.   
  6.     abstract int numberOfLegs();  
  7.   
  8. }  
のようなクラスを定義すると、AutoValue_Animalというクラスが生成される。
  1. final class AutoValue_Animal extends Animal {  
  2.   
  3.     private final String name;  
  4.     private final int numberOfLegs;  
  5.   
  6.     AutoValue_Animal(  
  7.             String name,  
  8.             int numberOfLegs) {  
  9.         if (name == null) {  
  10.             throw new NullPointerException("Null name");  
  11.         }  
  12.         this.name = name;  
  13.         this.numberOfLegs = numberOfLegs;  
  14.     }  
  15.   
  16.     @Override  
  17.     String name() {  
  18.         return name;  
  19.     }  
  20.   
  21.     @Override  
  22.     int numberOfLegs() {  
  23.         return numberOfLegs;  
  24.     }  
  25.   
  26.     @Override  
  27.     public String toString() {  
  28.         return "Animal{"  
  29.                 + "name=" + name + ", "  
  30.                 + "numberOfLegs=" + numberOfLegs  
  31.                 + "}";  
  32.     }  
  33.   
  34.     @Override  
  35.     public boolean equals(Object o) {  
  36.         if (o == this) {  
  37.             return true;  
  38.         }  
  39.         if (o instanceof Animal) {  
  40.             Animal that = (Animal) o;  
  41.             return (this.name.equals(that.name()))  
  42.                     && (this.numberOfLegs == that.numberOfLegs());  
  43.         }  
  44.         return false;  
  45.     }  
  46.   
  47.     @Override  
  48.     public int hashCode() {  
  49.         int h = 1;  
  50.         h *= 1000003;  
  51.         h ^= this.name.hashCode();  
  52.         h *= 1000003;  
  53.         h ^= this.numberOfLegs;  
  54.         return h;  
  55.     }  
  56.   
  57. }  
コンスタクタでは name と numberOfLegs を引数に取り、equals() や hasCode() ではこれらを使った実装になっている。 immutable value class なのでコンスタクタで受け取った引数は final として保持される。

abstract クラスに static な生成メソッドを用意して利用する。
  1. @AutoValue  
  2. abstract class Animal {  
  3.   
  4.     static Animal create(String name, int numberOfLegs) {  
  5.         return new AutoValue_Animal(name, numberOfLegs);  
  6.     }  
  7.   
  8.     abstract String name();  
  9.   
  10.     abstract int numberOfLegs();  
  11.   
  12. }  
コンスタクタの引数は null チェックされる。これを止めたいときは abstract メソッドの戻り値に @Nullable をつける。
  1. @AutoValue  
  2. abstract class Animal {  
  3.   
  4.     @Nullable  
  5.     abstract String name();  
  6.   
  7.     abstract int numberOfLegs();  
  8.   
  9. }  
  1. final class AutoValue_Animal extends Animal {  
  2.   
  3.     private final String name;  
  4.     private final int numberOfLegs;  
  5.   
  6.     AutoValue_Animal(  
  7.             @Nullable String name,  
  8.             int numberOfLegs) {  
  9.         this.name = name;  
  10.         this.numberOfLegs = numberOfLegs;  
  11.     }  
  12.   
  13.     ...  
  14. }  
Builder を用意することも可能。@AutoValue をつけるクラスにインナークラスとして abstract static な Builder クラスを定義し、@AutoValue.Builder をつける。
  1. @AutoValue  
  2. abstract class Animal {  
  3.   
  4.     abstract String name();  
  5.   
  6.     abstract int numberOfLegs();  
  7.   
  8.     static Builder builder() {  
  9.         return new AutoValue_Animal.Builder();  
  10.     }  
  11.   
  12.     @AutoValue.Builder  
  13.     abstract static class Builder {  
  14.         abstract Builder name(String value);  
  15.         abstract Builder numberOfLegs(int value);  
  16.         abstract Animal build();  
  17.     }  
  18. }  
  1. final class AutoValue_Animal extends Animal {  
  2.   
  3.     ...  
  4.   
  5.     static final class Builder extends Animal.Builder {  
  6.         private String name;  
  7.         private Integer numberOfLegs;  
  8.   
  9.         Builder() {  
  10.         }  
  11.   
  12.         Builder(Animal source) {  
  13.             this.name = source.name();  
  14.             this.numberOfLegs = source.numberOfLegs();  
  15.         }  
  16.   
  17.         @Override  
  18.         public Animal.Builder name(String name) {  
  19.             this.name = name;  
  20.             return this;  
  21.         }  
  22.   
  23.         @Override  
  24.         public Animal.Builder numberOfLegs(int numberOfLegs) {  
  25.             this.numberOfLegs = numberOfLegs;  
  26.             return this;  
  27.         }  
  28.   
  29.         @Override  
  30.         public Animal build() {  
  31.             String missing = "";  
  32.             if (name == null) {  
  33.                 missing += " name";  
  34.             }  
  35.             if (numberOfLegs == null) {  
  36.                 missing += " numberOfLegs";  
  37.             }  
  38.             if (!missing.isEmpty()) {  
  39.                 throw new IllegalStateException("Missing required properties:" + missing);  
  40.             }  
  41.             return new AutoValue_Animal(  
  42.                     this.name,  
  43.                     this.numberOfLegs);  
  44.         }  
  45.     }  
  46.   
  47. }