Edited at

Lombok 使い方メモ


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 になる。

  • さらに、各フィールドは自動で可視性が private になる。

  • まさに 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


参考