KEIS BLOGは株式会社ケイズ・ソフトウェアが運営しています。

KEIS BLOG

Java Tips -=-= Library =-=- Lombok Part 3

LINEで送る
[`evernote` not found]
Pocket

前回に引き続き画期的なライブラリ【Lombok】を紹介します。

Lombokとは

読み方は「ロンボック」または、「ロンボク」と読むようです。
クラスやメソッドにアノテーションをつけるだけで、Setter、Getter、toString、equalsなど繰り返し何度も書くコードを自動保管してくれます。
簡単に説明すると、ソースコード上SetterやGetterがなくても、コンパイルエラーにはならず、クラス外からSetterやGetterが利用できます。
更に、クラスファイルには変換済みのコードをベースにコンパイルされたコードが格納されているため、実行時にはLombokが必要ありません。

使用方法

Lombokで使える機能は以下の通りです。
この中から使い勝手の良さそうな機能をチョイスして、ご紹介していこうと思います。
各機能の解説は面白かったので、翻訳内容をそのまま載せています。

– val
手間のかからないfinal変数

– @NonNull
NullPointerExceptionを気にすることをやめ、愛する方法を学んだ

– @Cleanup
自動リソース管理:close()メソッドを面倒なく安全に呼び出します

– @Getter / @Setter
`public int getFoo() {return foo;}`を二度と書くことはありません。

– @ToString
あなたのフィールドを見るためにデバッガを起動する必要はありません:ロンボクにあなたのためのtoStringを生成させてください!

– @EqualsAndHashCode
平等化が容易:オブジェクトのフィールドからhashCodeを生成し、実装と同等になります。

– @NoArgsConstructor, @RequiredArgsConstructor and @AllArgsConstructor
順序付けられたコンストラクター:引数を取らないコンストラクター、最終フィールド/非NULLフィールドごとの1つの引数、またはすべてのフィールドの1つの引数を生成します。

– @Data
すべて一緒に:@ToString、@EqualsAndHashCode、すべてのフィールドで@Getter、およびすべての非最後のフィールドで@Setter、@RequiredArgsConstructorのショートカット!

– @Value
不変クラスは非常に簡単にできました。

– @Builder
ボブのおじさん:オブジェクト作成用の面倒なファンシーパンツ!

– @SneakyThrows
前に誰も投げ入れていない場所で、チェック例外を大胆に投げる!

– @Synchronized
同期が完了しました。ロックを公開しないでください。

– @Getter(lazy=true)
怠惰は美徳です!

– @Log
キャプテンズ・ログ、スターディート 24435.7:「その行は何だったの?」

まず始めに、`@EqualsAndHashCode`です。

– @EqualsAndHashCode
任意のクラスを定義し`@EqualsAndHashCode`アノテーションを付けることで、`equals(Object object)`メソッドと、`hashCode()`メソッドが自動で生成されます。
また、`exclude`オプションにより対象となるフィールドを除外することが出来ます。
下記サンプルだとコード量が3分の1程度に圧縮出来ます。

実際のコード(With Lombok)

import lombok.EqualsAndHashCode;

@EqualsAndHashCode(exclude={"id", "shape"})
public class EqualsAndHashCodeExample {
  private transient int transientVar = 10;
  private String name;
  private double score;
  private Shape shape = new Square(5, 10);
  private String[] tags;
  private int id;
  
  public String getName() {
    return this.name;
  }
  
  @EqualsAndHashCode(callSuper=true)
  public static class Square extends Shape {
    private final int width, height;
    
    public Square(int width, int height) {
      this.width = width;
      this.height = height;
    }
  }
}

同等の機能を持つコード(Vanilla Java)

import java.util.Arrays;

public class EqualsAndHashCodeExample {
  private transient int transientVar = 10;
  private String name;
  private double score;
  private Shape shape = new Square(5, 10);
  private String[] tags;
  private int id;
  
  public String getName() {
    return this.name;
  }
  
  @Override public boolean equals(Object o) {
    if (o == this) return true;
    if (!(o instanceof EqualsAndHashCodeExample)) return false;
    EqualsAndHashCodeExample other = (EqualsAndHashCodeExample) o;
    if (!other.canEqual((Object)this)) return false;
    if (this.getName() == null ? other.getName() != null : !this.getName().equals(other.getName())) return false;
    if (Double.compare(this.score, other.score) != 0) return false;
    if (!Arrays.deepEquals(this.tags, other.tags)) return false;
    return true;
  }
  
  @Override public int hashCode() {
    final int PRIME = 59;
    int result = 1;
    final long temp1 = Double.doubleToLongBits(this.score);
    result = (result*PRIME) + (this.name == null ? 43 : this.name.hashCode());
    result = (result*PRIME) + (int)(temp1 ^ (temp1 >>> 32));
    result = (result*PRIME) + Arrays.deepHashCode(this.tags);
    return result;
  }
  
  protected boolean canEqual(Object other) {
    return other instanceof EqualsAndHashCodeExample;
  }
  
  public static class Square extends Shape {
    private final int width, height;
    
    public Square(int width, int height) {
      this.width = width;
      this.height = height;
    }
    
    @Override public boolean equals(Object o) {
      if (o == this) return true;
      if (!(o instanceof Square)) return false;
      Square other = (Square) o;
      if (!other.canEqual((Object)this)) return false;
      if (!super.equals(o)) return false;
      if (this.width != other.width) return false;
      if (this.height != other.height) return false;
      return true;
    }
    
    @Override public int hashCode() {
      final int PRIME = 59;
      int result = 1;
      result = (result*PRIME) + super.hashCode();
      result = (result*PRIME) + this.width;
      result = (result*PRIME) + this.height;
      return result;
    }
    
    protected boolean canEqual(Object other) {
      return other instanceof Square;
    }
  }
}

最後に`@Data`です。

– @Data
このライブラリの中で最も多様するアノテーションの一つで、
任意のクラスに`@Data`アノテーションを定義することで、今まで解説した複数のアノテーションを付与したのと同様の働きをします。

以下のアノテーションを含みます:
@ToString
@EqualsAndHashCode
@Getter/@Setter
@RequiredArgsConstructor

@Getter/@Setterは全てのフィールドに対してPublicなSetterとGetterを生成します。
アクセスレベルを変える場合は明示的にアノテーションを定義し引数に`AccessLevel`を追加してください。

実際のコード(With Lombok)

import lombok.AccessLevel;
import lombok.Setter;
import lombok.Data;
import lombok.ToString;

@Data public class DataExample {
  private final String name;
  @Setter(AccessLevel.PACKAGE) private int age;
  private double score;
  private String[] tags;
  
  @ToString(includeFieldNames=true)
  @Data(staticConstructor="of")
  public static class Exercise<T> {
    private final String name;
    private final T value;
  }
}

同等の機能を持つコード(Vanilla Java)

import java.util.Arrays;

public class DataExample {
  private final String name;
  private int age;
  private double score;
  private String[] tags;
  
  public DataExample(String name) {
    this.name = name;
  }
  
  public String getName() {
    return this.name;
  }
  
  void setAge(int age) {
    this.age = age;
  }
  
  public int getAge() {
    return this.age;
  }
  
  public void setScore(double score) {
    this.score = score;
  }
  
  public double getScore() {
    return this.score;
  }
  
  public String[] getTags() {
    return this.tags;
  }
  
  public void setTags(String[] tags) {
    this.tags = tags;
  }
  
  @Override public String toString() {
    return "DataExample(" + this.getName() + ", " + this.getAge() + ", " + this.getScore() + ", " + Arrays.deepToString(this.getTags()) + ")";
  }
  
  protected boolean canEqual(Object other) {
    return other instanceof DataExample;
  }
  
  @Override public boolean equals(Object o) {
    if (o == this) return true;
    if (!(o instanceof DataExample)) return false;
    DataExample other = (DataExample) o;
    if (!other.canEqual((Object)this)) return false;
    if (this.getName() == null ? other.getName() != null : !this.getName().equals(other.getName())) return false;
    if (this.getAge() != other.getAge()) return false;
    if (Double.compare(this.getScore(), other.getScore()) != 0) return false;
    if (!Arrays.deepEquals(this.getTags(), other.getTags())) return false;
    return true;
  }
  
  @Override public int hashCode() {
    final int PRIME = 59;
    int result = 1;
    final long temp1 = Double.doubleToLongBits(this.getScore());
    result = (result*PRIME) + (this.getName() == null ? 43 : this.getName().hashCode());
    result = (result*PRIME) + this.getAge();
    result = (result*PRIME) + (int)(temp1 ^ (temp1 >>> 32));
    result = (result*PRIME) + Arrays.deepHashCode(this.getTags());
    return result;
  }
  
  public static class Exercise<T> {
    private final String name;
    private final T value;
    
    private Exercise(String name, T value) {
      this.name = name;
      this.value = value;
    }
    
    public static <T> Exercise<T> of(String name, T value) {
      return new Exercise<T>(name, value);
    }
    
    public String getName() {
      return this.name;
    }
    
    public T getValue() {
      return this.value;
    }
    
    @Override public String toString() {
      return "Exercise(name=" + this.getName() + ", value=" + this.getValue() + ")";
    }
    
    protected boolean canEqual(Object other) {
      return other instanceof Exercise;
    }
    
    @Override public boolean equals(Object o) {
      if (o == this) return true;
      if (!(o instanceof Exercise)) return false;
      Exercise<?> other = (Exercise<?>) o;
      if (!other.canEqual((Object)this)) return false;
      if (this.getName() == null ? other.getValue() != null : !this.getName().equals(other.getName())) return false;
      if (this.getValue() == null ? other.getValue() != null : !this.getValue().equals(other.getValue())) return false;
      return true;
    }
    
    @Override public int hashCode() {
      final int PRIME = 59;
      int result = 1;
      result = (result*PRIME) + (this.getName() == null ? 43 : this.getName().hashCode());
      result = (result*PRIME) + (this.getValue() == null ? 43 : this.getValue().hashCode());
      return result;
    }
  }
}

@EqualsAndHashCodeと@Dataの紹介でした。
@Dataは利用頻度が非常に高く、コーディングの助けになること間違いなしです。
次回も引き続き【Lombok】ご紹介いたします。

【関連記事】
Google App Engine 第一回
Google App Engine 第二回
Javaのライブラリを手軽にテストしたい!! Groovy入門 第1回
Google App Engine 第三回
Google App Engine 第四回
Google App Engine 第五回
Google App Engine 第六回
Google App Engine 第七回
Google App Engine 第八回
Google App Engine 第九回
Google App Engine 第十回
Google App Engine 第十一回
Google App Engine 第十二回
AngularJS入門01
AngularJS入門02
AngularJS入門03
AngularJS入門04
AngularJS入門05
AngularJS入門06
AngularJS入門07
AngularJS入門08
AngularJS入門09
攻略 Elevator Saga =基本編=
攻略 Elevator Saga =応用編=
Java Tips -=-= Library =-=- JodaTime [前半]
Java Tips -=-= Library =-=- JodaTime [後半]
Java Tips -=-= Library =-=- Lombok Part 1
Java Tips -=-= Library =-=- Lombok Part 2

LINEで送る
[`evernote` not found]
Pocket