Lombok 使い方メモ

  • 213
    Like
  • 0
    Comment
More than 1 year has passed since last update.

Lombok とは

  • 読みは、「ロンボック」もしくは「ロンボク」。
  • アノテーションを付けるだけで、 getter, setter, toString, equals などの「何度も繰り返し書くコード」をコンパイル時に自動生成してくれるようになる。
  • でも、 Getter, Setter の自動生成は無闇にやるとオブジェクト指向を破壊するので、「使ってるフレームワークがどうしても Getter, Setter を要求するから仕方ないんじゃい」ってとき以外は使うべきではないと思う。

Hello World

build.gradle
apply plugin: 'application'

configurations {
    provided
}

sourceSets {
    main { compileClasspath += configurations.provided }
}

repositories {
    mavenCentral()
}

dependencies {
    provided 'org.projectlombok:lombok:1.16.4'
}

mainClassName = 'sample.lombok.Main'
Main.java
package sample.lombok;

import lombok.Data;

@Data
public class Main {

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

        m.setString("Hello Lombok!!");
        m.setNumber(999);

        System.out.println(m);
    }

    private String string;
    private int number;
}
動作確認
$ gradle -q run
Main(string=Hello Lombok!!, number=999)
  • @Data でクラスをアノテートすることで、 getter, setter, toString などのメソッドが自動生成されている。
  • Lombok 自体はコンパイル時にのみ使用するので、依存のスコープは provided にする。

IDE で使えるようにする

Lombok のダウンロード

こちら から、 Lombok の jar ファイルをダウンロードする。

Eclipse の場合

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

Hello World を Eclipse に取り込んでみる。

build.gradle
apply plugin: 'application'
+ apply plugin: 'eclipse'

configurations {
    provided
}

sourceSets {
    main { compileClasspath += configurations.provided }
}

repositories {
    mavenCentral()
}

dependencies {
    provided 'org.projectlombok:lombok:1.16.4'
}

mainClassName = 'sample.lombok.Main'

+ eclipse {
+     project.name = 'lombok-sample'
+ 
+     classpath {
+         plusConfigurations += [configurations.provided]
+         noExportConfigurations += [configurations.provided]
+     }
+ }
Eclipseプロジェクト化
$ gradle eclipse

既存プロジェクトとして Eclipse に取り込む。

lombok.JPG

Lombok が自動生成したメソッドが認識されている。

NetBeans の場合

※バージョンは 8.0.2

Gradle プロジェクトの場合

特別な設定は必要ない。
NetBeans に Gradle プラグインを入れていれば、前述の Gradle プロジェクトを取り込むだけで有効になっている。

lombok.JPG

普通のプロジェクトの場合

  • プロジェクトの依存ライブラリに lombok.jar を追加する。
  • プロジェクトのプロパティを開き、[ビルド] > [コンパイル] の「注釈処理を有効にする」と「エディタでの注釈処理を有効にする」にチェックを入れる。

val 変数

Main.java
package sample.lombok;

import java.util.Arrays;
import java.util.HashMap;

import lombok.val;


public class Main {

    public static void main(String... args) {
        val list = Arrays.asList("hoge", "fuga", "piyo");
        list.forEach(System.out::println);

        val map = new HashMap<String, Long>();
        map.put("hoge", 1L);
    }
}
  • val という型で変数を定義すると、代入した値から良しなに型推論してくれる。

lombok.JPG

  • 総称型もちゃんと読み取ってくれる。

lombok.JPG

  • val で定義した変数は final 修飾されているので、再代入はできない。

@NonNull

Main.java
package sample.lombok;

import lombok.NonNull;

public class Main {

    public static void main(String... args) {
        method("hoge");
        method(null);
    }

    private static void method(@NonNull String value) {
        System.out.println(value);
    }
}
実行結果
hoge
Exception in thread "main" java.lang.NullPointerException: value
    at sample.lombok.Main.method(Main.java:13)
    at sample.lombok.Main.main(Main.java:10)
  • @NonNull でメソッドの引数をアノテートすると、 null チェックが自動生成される。

@Cleanup

Main.java
package sample.lombok;

import lombok.Cleanup;

public class Main {

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

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

    public void close(String arg) {
        System.out.println("close(String) メソッドが呼ばれました");
    }
}
実行結果
close メソッドが呼ばれました
  • @Cleanup でローカル変数をアノテートすると、スコープから抜けるときに close() メソッドが呼ばれるようになる。
実行するメソッドを指定する
package sample.lombok;

import lombok.Cleanup;

public class Main {

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

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

    public void dispose() {
        System.out.println("dispose メソッドが呼ばれました");
    }
}
実行結果
dispose メソッドが呼ばれました
  • value で実行するメソッドを指定できる。

@Getter, @Setter

基本

Main.java
package sample.lombok;

import lombok.Getter;
import lombok.Setter;

public class Main {

    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;
}
実行結果
Hello @Getter, @Setter
  • @Getter でゲッターメソッドを、 @Setter でセッターメソッドを自動生成できる。

可視性を指定する

Main.java
package sample.lombok;

import lombok.AccessLevel;
import lombok.Getter;

public class Main {

    @Getter(AccessLevel.PRIVATE)
    private String value;
}

lombok.JPG

  • valueAccessLevel を渡すことで可視性を指定できる。

@Getter(lazy=true)

Main.java
package sample.lombok;

import lombok.Getter;

public class Main {

    public static void main(String[] args) {
        Main m = new Main();
        System.out.println("Main instance is created");
        m.getLazy();
    }

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

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

    private String createValue(String name) {
        System.out.println("createValue(" + name + ")");
        return null;
    }
}
実行結果
createValue(not lazy)
Main instance is created
createValue(lazy)
  • @Getterlazy に true を設定すると、値の初期化をゲッターメソッドが最初に呼ばれる時まで遅延させることができる。

@ToString

Main.java
package sample.lombok;

import java.util.Arrays;
import java.util.List;

import lombok.ToString;

@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;
}
実行結果
Main(id=100, value=hoge, list=[fizz, buzz])
  • @ToString でクラスをアノテートすると、 toString() メソッドが自動生成される。
  • exclude 属性で、出力しないフィールドを指定できる。
    • クラスが相互依存してると toString() したときに無限ループが発生するので、 exclude で除外しておく必要がある。

@EqualsAndHashCode

Main.java
package sample.lombok;

import java.util.Arrays;
import java.util.List;

import lombok.EqualsAndHashCode;

@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");
}
実行結果
a.hash = 290324031
b.hash = 290324031
true
  • @EqualsAndHashCode でアノテートすると、 equals() メソッドと hashCode() メソッドが自動生成される。
  • 比較は、全てのフィールドがそれぞれ一致しているかどうかで行われる。
    • DDD だと値オブジェクトに使えるかと。

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

@NoArgsConstructor

Main.java
package sample.lombok;

import lombok.NoArgsConstructor;

@NoArgsConstructor
public class Main {

    public Main(String string) {}
}

lombok.JPG

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

@RequiredArgsConstructor

Main.java
package sample.lombok;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
public class Main {

    private String optional;
    private final int required;
}

lombok.JPG

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

@AllArgsConstructor

Main.java
package sample.lombok;

import lombok.AllArgsConstructor;

@AllArgsConstructor
public class Main {

    private String string;
    private int number;
}

lombok.JPG

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

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

Main.java
package sample.lombok;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor(staticName="of")
public class Main {

    private final String required;
    private int optional;
}

lombok.JPG

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

@Data

Main.java
package sample.lombok;

import lombok.Data;

@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;
}
実行結果
a = Main(required=data, number=100)
true
  • @Data でクラスをアノテートすると、以下のアノテーションを全て設定したのと同じ効果を得られる。
    • @ToString
    • @Getter
    • @Setter
    • @RequiredArgsConstructor
    • @EqualsAndHashCode

@Value

Main.java
package sample.lombok;

import lombok.Value;

@Value
public class Main {

    String string;
    int number;
}

lombok.JPG

  • @Value でアノテートすることで、以下のアノテーションを設定したのと同じ効果を得られる。
    • @Getter
    • @ToString
    • @EqualsAndHashCode
    • @AllArgsConstructor
  • さらに、クラスおよび各フィールドは final になる。
  • さらに、各フィールドは自動で可視性が priavte になる。
  • まさに DDD の値オブジェクトか。

@Builder

Main.java
package sample.lombok;

import java.util.Arrays;
import java.util.List;

import lombok.Builder;
import lombok.ToString;

@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;
}
実行結果
Main(string=test, number=100, list=[fizz, buzz])
  • @Builder でアノテートすることで、そのクラスのビルダークラスを自動生成できる。

@Singular

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

Main.java
package sample.lombok;

import java.util.Arrays;
import java.util.List;

import lombok.Builder;
import lombok.Singular;
import lombok.ToString;

@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;
}
Main.java
Main(string=test, number=100, list=[hoge, fuga, fizz, buzz])

@SneakyThrows

Main.java
package sample.lombok;

import lombok.SneakyThrows;

public class Main {

    public static void main(String[] args) {
        method();
    }

    @SneakyThrows
    private static void method() {
        throw new Exception("test");
    }
}
実行結果
Exception in thread "main" java.lang.Exception: test
    at sample.lombok.Main.method(Main.java:13)
    at sample.lombok.Main.main(Main.java:8)
  • @SneakyThrows でメソッドをアノテートすると、チェック例外が内部でスローされていても throws 句を書かなくて良くなる。
    • sneaky は「コソコソする」という意味。

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

Main.java
package sample.lombok;

import java.io.IOException;

import lombok.SneakyThrows;

public class Main {

    public static void main(String[] args) {
        method();
    }

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

        throw new IOException();
    }
}
  • value で例外の Class オブジェクトを渡すことで、指定した例外だけを無視できるようになる。

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

build.gradle
dependencies {
    provided 'org.projectlombok:lombok:1.16.4'
+   compile 'org.slf4j:slf4j-simple:1.7.12'
}
Main.java
package sample.lombok;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class Main {

    public static void main(String[] args) {
        log.info("Hello Logger!!");
    }
}
動作確認
[main] INFO sample.lombok.Main - Hello Logger!!
  • @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

参考