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 |