/ shirokurostone::memo / archives

Lombokアノテーションのメモ

2018-07-08

Javaのコンパイル時に定型コードを自動生成してくれるLombokをいろいろ触ってみたのでメモする。

@NonNull

メソッドの引数に付与してnullチェックを生成する。 メソッドの先頭でnullかどうか確認し、nullの場合はNullPointerExceptionをthrowする。コンストラクタの場合はsuper()this()の呼び出しの後にチェックされる。

生成前

    public void hoge(@NonNull Object fuga){
        System.out.println(fuga);
    }

生成後

    public void hoge(@NonNull Object fuga) {
        if (fuga == null) {
            throw new NullPointerException("fuga is marked @NonNull but is null");
        } else {
            System.out.println(fuga);
        }
    }

@Cleanup

ローカル変数の宣言に付与して、スコープを抜ける前にオブジェクトのクリーンアップ処理を追加する。

具体的にはtry-finally節の追加とfinally節中にclose()メソッドの呼び出しを追加する。@Cleanupの引数にメソッド名を文字列で指定して、close()メソッド以外を呼ぶこともできる。

生成前

    public void hoge() throws IOException {
        @Cleanup BufferedReader reader = new BufferedReader(new FileReader("testfile.txt"));
        System.out.println(reader.readLine());
    }

生成後

    public void hoge() throws IOException {
        BufferedReader reader = new BufferedReader(new FileReader("testfile.txt"));
        try {
            System.out.println(reader.readLine());
        } finally {
            if (Collections.singletonList(reader).get(0) != null) {
                reader.close();
            }
        }
    }

@Getter/@Setter

フィールドに付与してgetterとsetterを生成する。メソッド名はフィールド名の先頭を大文字にしてget/setをつけたものが生成されるが、フィールドの型がbooleanの場合、getterメソッド名はgetではなくisをつけた名前になる。

可視性はデフォルトでpublicになるが、@Getter/@Setterの引数にAccessLevel.*を指定すると変更することができる。

クラス定義にアノテーションを付与した場合、インスタンスフィールド全てが生成対象になる。 生成したくないフィールドがある場合、AccessLevel.NONEをフィールドに付与すると生成されなくなる。

生成前

    @Setter
    @Getter
    private int hoge;

    @Getter(AccessLevel.PACKAGE)
    private boolean fuga;

生成後

    private int hoge;
    private boolean fuga;

    public void setHoge(int hoge) {
        this.hoge = hoge;
    }

    public int getHoge() {
        return this.hoge;
    }

    boolean isFuga() {
        return this.fuga;
    }

@ToString

クラス定義に付与して、toString()メソッドの実装を生成する。 デフォルトでは、クラス名(フィールド名1=値, フィールド名2=値,...)形式の文字列を返す。アノテーションの引数にincludeFieldNames=falseを追加するとフィールド名を取り除くことができ、callSuper=trueを指定すると親クラスのtoString()メソッドの結果を含めることができる。

デフォルトではインスタンスフィールド全てが文字列の生成対象となる。除外したい場合はフィールドに@ToString\.Excludeアノテーションを付与する。もしくはクラス定義の@ToStringonlyExplicitlyIncluded=trueを指定して表示したいフィールドのみ@ToString\.Includeを付与することで、表示するフィールドを制御できる。

引数なしのインスタンスメソッドに@ToString\.Includeを付与することで、メソッドの実行結果も生成文字列に含めることもできる。

生成前

    @ToString(callSuper=true)
    public class Sandbox {
        private int hoge;
        private boolean fuga;
    }

生成後

    public String toString() {
        return "Sandbox(super=" + super.toString() + ", hoge=" + this.hoge + ", fuga=" + this.fuga + ")";
    }

@EqualsAndHashCode

equals()hashCode()メソッドを生成する。

生成前

@EqualsAndHashCode
public class Sandbox {
    @NonNull private Integer hoge;
    private boolean fuga;
}

生成後

長いので省略。equals()はフィールド同士をequals()メソッドで比較する。

コンストラクタ生成(@NoArgsConstructor, @RequiredArgsConstructor, @AllArgsConstructor)

@NoArgsConstructor

引数なしのコンストラクタを生成する。 未初期化のfinalフィールドを持つ場合コンパイル時エラーとなるが、force=trueを指定した場合は0かfalseかnullで初期化される。@NonNullを付与したフィールドがあった場合でもnullチェック処理は生成されないので注意が必要。

生成前
@NoArgsConstructor()
public class Sandbox {
    @NonNull private Integer hoge;
    private boolean fuga;
}
生成後
public class Sandbox {
    @NonNull
    private Integer hoge;
    private boolean fuga;

    public Sandbox() {
    }
}

@RequiredArgsConstructor

すべての初期化されていないfinalフィールド・@NonNullが付与されたフィールドを引数に持つコンストラクタを生成する。引数の順序はクラス内の出現順と同じ。 @NonNullのnullチェックは生成される。

生成前
@RequiredArgsConstructor()
public class Sandbox {
    @NonNull private Integer hoge;
    private boolean fuga;
}
生成後
public class Sandbox {
    @NonNull
    private Integer hoge;
    private boolean fuga;

    public Sandbox(@NonNull Integer hoge) {
        if (hoge == null) {
            throw new NullPointerException("hoge is marked @NonNull but is null");
        } else {
            this.hoge = hoge;
        }
    }
}

@AllArgsConstructor

全てのフィールドを引数に持つコンストラクタを生成する。 @NonNullのnullチェックは生成される。

生成前
@AllArgsConstructor()
public class Sandbox {
    @NonNull private Integer hoge;
    private boolean fuga;
}
生成後
public class Sandbox {
    @NonNull
    private Integer hoge;
    private boolean fuga;

    public Sandbox(@NonNull Integer hoge, boolean fuga) {
        if (hoge == null) {
            throw new NullPointerException("hoge is marked @NonNull but is null");
        } else {
            this.hoge = hoge;
            this.fuga = fuga;
        }
    }
}

@Data

@ToString, @EqualsAndHashCode, @Getter, @Setter, @RequiredArgsConstructorを指定した場合と同じ。

生成前

@Data
public class Sandbox {
    @NonNull private Integer hoge;
    private boolean fuga;
}

生成後

長いので省略。生成されたメソッドは次の通り。

@Value

@Dataをimmutableにしたもの。すべてのフィールドはprivate finalに設定され、Setterは生成されない。 クラスにもfinalが設定されるが@NonFinalを指定していると設定されない。 生成されるメソッドが先に定義されていた場合は、メソッドが生成されないだけでエラーは発生しない。

生成前

@Value
public class Sandbox {
    @NonNull private Integer hoge;
    private boolean fuga;
}

生成後

こちらも省略。

@Builder

クラス定義に付与してBuilderクラスを生成する。生成するコードは次の通り。

生成前

@Builder
public class Sandbox {
    @NonNull private Integer hoge;
    private boolean fuga;
}

生成後

public class Sandbox {
    @NonNull
    private Integer hoge;
    private boolean fuga;

    Sandbox(@NonNull Integer hoge, boolean fuga) {
        if (hoge == null) {
            throw new NullPointerException("hoge is marked @NonNull but is null");
        } else {
            this.hoge = hoge;
            this.fuga = fuga;
        }
    }

    public static Sandbox.SandboxBuilder builder() {
        return new Sandbox.SandboxBuilder();
    }

    public static class SandboxBuilder {
        private Integer hoge;
        private boolean fuga;

        SandboxBuilder() {
        }

        public Sandbox.SandboxBuilder hoge(Integer hoge) {
            this.hoge = hoge;
            return this;
        }

        public Sandbox.SandboxBuilder fuga(boolean fuga) {
            this.fuga = fuga;
            return this;
        }

        public Sandbox build() {
            return new Sandbox(this.hoge, this.fuga);
        }

        public String toString() {
            return "Sandbox.SandboxBuilder(hoge=" + this.hoge + ", fuga=" + this.fuga + ")";
        }
    }
}

@Synchronized

メソッドに付与して、指定したオブジェクトでロックするsynchronizedブロックを追加する。 デフォルトの場合、インスタンスメソッドは$lockフィールド、クラスメソッドは$Lockクラスフィールドが使用される。 存在しない場合はprivateで作成される。

別のフィールドでロックしたい場合は@Synchronizedの引数にフィールド名を文字列で指定する。

生成前

    @Synchronized
    public void hoge(){
        System.out.println("hoge");
    }

    @Synchronized
    public static void fuga(){
        System.out.println("fuga");
    }

生成後

    private final Object $lock = new Object[0];
    private static final Object $LOCK = new Object[0];

    public void hoge() {
        synchronized(this.$lock) {
            System.out.println("hoge");
        }
    }

    public static void fuga() {
        synchronized($LOCK) {
            System.out.println("fuga");
        }
    }

@Log

クラス定義に付与すると、static finalなlogフィールドを生成し指定したロガーで初期化する。各ロガー実装に応じたアノテーションが用意されている。

生成前

@Log
public class Sandbox {
    @NonNull private Integer hoge;
    private boolean fuga;
}

生成後

    private static final Logger log = Logger.getLogger(Sandbox.class.getName());

参考