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 が自動生成したメソッドが認識されている。
NetBeans の場合
※バージョンは 8.0.2
Gradle プロジェクトの場合
特別な設定は必要ない。
NetBeans に Gradle プラグインを入れていれば、前述の Gradle プロジェクトを取り込むだけで有効になっている。
普通のプロジェクトの場合
- プロジェクトの依存ライブラリに
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という型で変数を定義すると、代入した値から良しなに型推論してくれる。
- 総称型もちゃんと読み取ってくれる。
-
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;
}
-
valueにAccessLevelを渡すことで可視性を指定できる。
@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)
-
@Getterのlazyに 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で除外しておく必要がある。
- クラスが相互依存してると toString() したときに無限ループが発生するので、
@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) {}
}
-
@NoArgsConstructorでアノテートすることで、引数なしのコンストラクタを定義できる。
@RequiredArgsConstructor
Main.java
package sample.lombok;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public class Main {
private String optional;
private final int required;
}
-
@RequiredArgsConstructorでアノテートすることで、finalで修飾されたフィールドだけを引数に受け取るコンストラクタを自動生成できる。
@AllArgsConstructor
Main.java
package sample.lombok;
import lombok.AllArgsConstructor;
@AllArgsConstructor
public class Main {
private String string;
private int number;
}
-
@AllArgsConstructorでアノテートすることで、全てのフィールドを引数に受け取るコンストラクタを自動生成できる。
static なファクトリメソッドを定義する
Main.java
package sample.lombok;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor(staticName="of")
public class Main {
private final String required;
private int optional;
}
- 各アノテーションで
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;
}
-
@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 |









