Java特有の冗長なコードを簡潔に記述する「Lombok」

Java特有の単調なコードを簡潔に記述する

目次

Lombok とは

Lombok は、 Java言語におけるボイラープレートコードをソースコードから排除するために使用するライブラリです。 ボイラープレートコードとは、何度も繰り返し書く言語仕様上省く事ができない定型的なコードの事で、本質的なロジックでないためアプリケーションを実装する上で冗長なコードとなってしまいます。

Java言語における代表的なボイラープレートコード

  • メンバー変数にアクセスするための getter / setter メソッド
  • equals/hashCodeメソッド
  • toStringメソッド
  • コンストラクタ
  • リソース(入出力ストリーム等)のクローズ処理
  • ロガーインスタンスの生成

Lombokは、これらのボイラープレートコードをコンパイル時に生成することで、 開発者が実装するソースコード上から冗長なコードを取り除く仕組みを提供しています。

Lombokを使用して作成したJavaBeanのソースコード

import lombok.Data;

@Data
public class SampleForm {
    String id;
    String name;
}

クラスレベルに@lombok.Dataアノテーションを付与するだけで、 JavaBeanとして必要なメソッドがLombokによって生成されます。

これは、Lombokの@Dataアノテーションを付与しただけで、約10行のソースコードから、 約60行ある下記のソースコード(Eclipseの自動生成機能を使用して出力したソースコード)によって生成されるクラスと同じ効果を得る事ができる事を意味しています。

public class Sample {

    private Integer id;
    private String name;

    public Sample() {
    }

    public Integer getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    @Override
    public int hashCode() {
        final int id = 31;
        int result = 1;
        result = id * result
                + ((name == null) ? 0 : name.hashCode());
        result = id * result + ((name == null) ? 0 : id.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Sample other = (Sample) obj;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        if (id == null) {
            if (other.id != null)
                return false;
        } else if (!id.equals(other.id))
            return false;
        return true;
    }

    @Override
    public String toString() {
        return "User [id=" + id + ", name=" + name + "]";
    }

}

IDEと Lombok を連携する

LombokをIDE上で使用する場合は、IDEが提供するコンパイル(ビルド)機能と連携するために、 LombokをIDEにインストールする必要があります。

Lombokのインストール

Lombokのjarファイルは、以下のどちらかから取得します。

Eclipse の場合

  1. ダウンロードした lombok.jar は実行可能 jar になっているので、ダブルクリックなどで起動する。
  2. Lombok のインストーラが起動するので、 Specify location... をクリックする。
  3. eclipse.exe が存在するフォルダを選択する。
  4. Install / Update をクリックする。

代表的なLombokの機能

val 変数

val という型で変数を定義することで、代入した値から良しなに型推論してくれるようになります。総称型も読み取る事が可能です。val で定義した変数は final 修飾されているので、再代入はできません。

val map = new HashMap<String, Long>();
map.put("hoge", 1L);

@NonNull

@NonNull でメソッドの引数をアノテートすると、 null チェックが自動生成されます。

private static void method(@NonNull String value) {
    System.out.println(value);
}

@Cleanup

@Cleanup でローカル変数をアノテートすると、スコープから抜けるときに close() メソッドが呼ばれるようになります。

public static void main(String... args) {
    @Cleanup Main m = new Main();
}

public void close() {
    System.out.println("close メソッドが呼ばれました");
}

@Cleanup の引数を指定すると 実行するメソッドを指定することも出来ます。

public static void main(String... args) {
    @Cleanup("dispose") Main m = new Main();
}

public void dispose() {
    System.out.println("dispose メソッドが呼ばれました");
}

@Getter, @Setter

@Getter でゲッターメソッドを、 @Setter でセッターメソッドが自動生成されます。

public static void main(String... args) {
    Main m = new Main();

   m.setValue("Hello @Getter, @Setter");
   System.out.println(m.getValue());
}

@Getter @Setter
private String value;

value に AccessLevel を渡すことで可視性を指定する

@Getter(AccessLevel.PRIVATE)
private String value;

値の初期化をゲッターメソッドが最初に呼ばれる時まで遅延させる

@Getter(lazy=true)
private final String lazy = createValue("lazy");

@ToString

@ToString でクラスをアノテートすると、 toString() メソッドが自動生成されます。 また、exclude 属性で、出力しないフィールドを指定する事ができます。

@ToString(exclude="ignore")
public class Main {

    public static void main(String[] args) {
        System.out.println(new Main());
    }

    private int id = 100;
    private String value = "hoge";
    private List<String> list = Arrays.asList("fizz", "buzz");
    private double ignore = 999;
}

@EqualsAndHashCode

@EqualsAndHashCode でアノテートすると、 equals() メソッドと hashCode() メソッドが自動生成されます。 比較は、全てのフィールドがそれぞれ一致しているかどうかで行われます。

@EqualsAndHashCode
public class Main {

    public static void main(String[] args) {
        Main a = new Main();
        Main b = new Main();

        System.out.println("a.hash = " + a.hashCode());
        System.out.println("b.hash = " + b.hashCode());
        System.out.println(a.equals(b));
    }

    private int id = 100;
    private String value = "hoge";
    private List<String> list = Arrays.asList("fizz", "buzz");
}

コンストラクタの自動生成

@NoArgsConstructor

@NoArgsConstructor でアノテートすることで、引数なしのコンストラクタを定義できます。

@NoArgsConstructor
public class Main {
    public Main(String string) {}
}

@RequiredArgsConstructor

@RequiredArgsConstructor でアノテートすることで、 final で修飾されたフィールドだけを引数に受け取るコンストラクタを自動生成できます。

@RequiredArgsConstructor
public class Main {
    private String optional;
    private final int required;
}

@AllArgsConstructor

@AllArgsConstructor でアノテートすることで、全てのフィールドを引数に受け取るコンストラクタを自動生成できます。

@AllArgsConstructor
public class Main {
    private String string;
    private int number;
}

static なファクトリメソッドを定義する

各アノテーションで staticName 属性を指定することで、 static なファクトリメソッドを自動生成できます。

@RequiredArgsConstructor(staticName="of")
public class Main {
    private final String required;
    private int optional;
}

@Data

@Data でクラスをアノテートすると、以下のアノテーションを全て設定したのと同じ効果を得られる。

@ToString
@Getter
@Setter
@RequiredArgsConstructor
@EqualsAndHashCode 
@Data
public class Main {

    public static void main(String[] args) {
        Main a = new Main("data");
        a.setNumber(100);

        Main b = new Main("data");
        b.setNumber(100);

        System.out.println("a = " + a);
        System.out.println(a.equals(b));
    }

    private final String required;
    private int number;
}

@Value

@Value でアノテートすることで、以下のアノテーションを設定したのと同じ効果を得られます。また、クラスおよび各フィールドは final となり、 各フィールドは自動で可視性が private になります。

@Getter
@ToString
@EqualsAndHashCode
@AllArgsConstructor  
@Value
public class Main {
    String string;
    int number;
}

@Builder

@Builder でアノテートすることで、そのクラスのビルダークラスを自動生成できます。

@Builder
@ToString
public class Main {

    public static void main(String[] args) {
        MainBuilder builder = Main.builder()
                                  .string("test")
                                  .number(100)
                                  .list(Arrays.asList("hoge", "fuga"))
                                  .list(Arrays.asList("fizz", "buzz"));

        Main m = builder.build();

        System.out.println(m);
    }

    private String string;
    private int number;
    private List<String> list;
}

@Singular

デフォルトのままだと、コレクション型のフィールドも普通に上書きセッターメソッドとして自動生成されます。追加メソッドとして自動生成したい場合は @Singular でフィールドをアノテートします。

@Builder
@ToString
public class Main {

    public static void main(String[] args) {
        MainBuilder builder = Main.builder()
                                  .string("test")
                                  .number(100)
                                  .list("hoge")
                                  .list("fuga")
                                  .list(Arrays.asList("fizz", "buzz"));

        Main m = builder.build();

        System.out.println(m);
    }

    private String string;
    private int number;
    @Singular("list")
    private List<String> list;
}

@SneakyThrows

@SneakyThrows でメソッドをアノテートすると、チェック例外が内部でスローされていても throws 句を書かなくて良くなります。

@SneakyThrows
private static void method() {
    throw new Exception("test");
}

無視できる例外を指定する

value で例外の Class オブジェクトを渡すことで、指定した例外だけを無視できるようになります。

@SneakyThrows(IOException.class)
private static void method() {
    try {
        throw new Exception("test");
    } catch (Exception e) {
        // catch しないとコンパイルエラー
    }
    throw new IOException();
}

ロガーを使えるようにする

@Slf4j でクラスをアノテートすることで、 log という名前の static final なロガーが使えるようなります。 Slf4j 以外にも、以下のロガーに対応している。

アノテーション ロガークラス
@CommonsLog org.apache.commons.logging.Log
@Log org.apache.commons.logging.Log
@Log4j org.apache.log4j.Logger
@Log4j2 org.apache.logging.log4j.Logger
@Slf4j org.slf4j.Logger
@XSlf4j org.slf4j.ext.XLogger
@Slf4j
public class Main {

    public static void main(String[] args) {
        log.info("Hello Logger!!");
    }
}

参考

コメントを残す

入力エリアすべてが必須項目です。メールアドレスが公開されることはありません。

内容をご確認の上、送信してください。