lombok使用概要
29711 ワード
前提条件
この文章は主にlombokの使用を紹介して、lombokのソースコードと原理についてはしばらく探究しないで、前の文章のプラグイン化注釈処理APIを見てlombokの基本原理を理解することができます.参考資料: lombok公式サイト lombok公式チュートリアル-lombokすべての特性 概要
Project Lombokはjavaライブラリで、エディタに自動的に挿入し、javaをより豊かにするためのツールを構築します.もうgetterやequalsメソッドは使いません.valなど、将来のjavaプロパティにできるだけ早くアクセスします.これがlombokの公式プロフィールです(例えばJdk 9に追加されたvalキーワードはlombokに早く登場しました).lombokは実際にはJSR-269のプラグイン化注釈処理APIに基づいており、コンパイル中に特定の注釈を使用したクラス、メソッド、属性またはコードフラグメントを動的に修正し、カスタム機能を追加または実現するクラスライブラリである.
インストール
maven依存
プロジェクトでlombokに使用する注釈は、その依存性を導入する必要があります.
現在の最新バージョンは1.16.20です.
プラグイン
IDEにlombokのプラグインをインストールしないと、IDEはlombokコンパイル中に動的に生成されたコードを認識できず、コードブロックの赤として表現されるため、そのプラグインをインストールする必要があります.プラグインアドレス:https://projectlombok.org/download.
ダウンロードが完了すると、jarパッケージlombok-xx.xx.xx.jarが得られます.コマンドjava-jar lombok-xx.xx.xx.jarを直接使用して実行し、IDEA、eclipse、myeclipseにインストールします.
lombok注記
@val
@valは、修飾子がfinalのローカル変数タイプであることを宣言するために使用され、実際のタイプを記述する必要はありません.これは、コンパイラのタイプに依存します.@valの役割ドメインは局所変数である.例を次に示します.
lombokの使用:
オリジナルJavaに相当:
@var
@varの機能は@valに似ていますが、@varは非final修飾の局所変数を修飾します.
@NonNull
@NonNullの役割ドメインは、属性(field)、メソッドパラメータまたは、コンストラクション関数パラメータ、およびローカル変数(LOCAL_VARIABLE)であり、これらのパラメータに空のチェック文を追加します.基本フォーマットは:
lombokの使用:
オリジナルJavaに相当:
@Cleanup
@Cleanupを使用すると、コード実行パスが現在の範囲を終了する前に、指定されたリソースが自動的にクリーンアップされることを確認できます.一般的には、ストリームのローカル変数のクローズを使用します.value()でリソースを閉じるメソッド名を指定できます.リソースを閉じるメソッドは、voidメソッドを参照しないでください.デフォルトのリソースを閉じるメソッド名はcloseです.
lombokの使用:
オリジナルJavaに相当:
@Getter/@Setter
@Getter/@Setter役割ドメインは属性またはクラスです.@Getterは指定された属性またはクラス内のすべての属性に対してGetterメソッドを生成し、@Setterは非final属性またはクラス内のすべての非final属性に対してSetterメソッドを生成することを指定します.生成方法の修飾子は@Getter/@Setterのvalue()のAccessLevel属性で指定でき、@Getterのブール値属性lazyで遅延ロードの有無を指定できます.
lombokの使用:
オリジナルJavaに相当:
@ToString
@ToStringの役割ドメインはクラスで、主な役割はクラス内のtoStringメソッドを上書きすることです.@ToStringの属性は比較的多く、以下のように紹介されています. includeFieldName():ブール値、デフォルトはtrue、trueはtoStringを接続するときに属性名を使用することを示します. exclude():文字列配列、デフォルトは空で、属性名でtoStringをパッチするときに使用される属性を除外します. of():exclude()属性の対立属性であり、includeを意味する. callSuper():ブール値、デフォルトfalse、親クラスのプロパティを呼び出すかどうか. doNotUseGetters():ブール値、デフォルトfalse、trueは、Getterメソッドではなく、パッチtoStringの場合に属性値を使用することを示します.
次は公式の例です.
lombokの使用:
オリジナルJavaに相当:
@EqualsAndHashCode
@EqualsAndHashCodeの役割ドメインはクラスであり、equals()メソッドとhashCode()メソッドを生成するために使用されます.この注記のプロパティは多いですが、@ToStringと似ています.例を次に示します.
lombokの使用:
オリジナルJavaに相当:
@NoArgsConstructor, @RequiredArgsConstructor, @AllArgsConstructor
この3つの注釈の役割ドメインはすべてクラスです.@NoArgsConstructorの役割は、パラメータのない構造関数を生成することです.@AllArgsConstructorの役割は、すべてのフィールドを持つコンストラクション関数を生成することです.@RequiredArgsConstructorの役割はfinalまたは@NonNull修飾を使用した属性ごとに1つのパラメータしかない構造関数を生成することです.この3つの注記は、staticName()で構造の名前を指定し、access()で構造の修飾子を指定できます.@NoArgsConstructorの属性force()のデフォルト値はfalseであり、クラスにfinal属性が存在する場合に@NoArgsConstructorを使用するとコンパイルエラーが発生し、force()値がtrueの場合、final修飾属性は「パラメータなし構造」のパラメータとして使用され、属性は0、nullまたはfalseに割り当てられます.
lombokの使用:
オリジナルJavaに相当:
@Data
@Dataの役割ドメインはクラスであり,@Getter,@Setter,@ToString,@EqualsAndHashCode,@RequiredArgsConstructorの同時適用に相当する.カスタムコンストラクション関数がすでに表示されている場合は、コンストラクション関数は自動的に生成されません.例を次に示します.
lombokの使用:
オリジナルJavaに相当:
@Value
@Valueの役割ドメインはクラスであり、@Dataと似ていますが、可変タイプに使用されます.生成されたクラスとすべてのフィールドがfinalに設定され、すべてのフィールドがprivateで、自動的にGetterが生成されますが、Setterがなく、すべてのフィールドを初期化するコンストラクション関数が生成されます.final,@ToString,@EqualsAndHashCode,@AllArgsConstructor,@FieldDefaults(makeFinal=true,level=AccessLevel.PRIVATE)と@Getterを同時に適用したものに相当する.
lombokの使用:
オリジナルJavaに相当:
@Builder
@Builderの役割ドメインはクラスです.この注釈を使用してクラスにメンバークラス(Builder)を追加すると、ビルダーモードが使用され、コンパイル時にBuilder内部クラスとフィールド全体のコンストラクタが追加されます[email protected]はBuilderのプロパティのデフォルト値を指定するために使用されます.@Singularはlombokの現在のプロパティタイプがセットタイプであることを示します.lombokは信頼できるセットタイプにsetterメソッドではなくadderメソッドを追加します.
lombokの使用:
オリジナルJavaに相当:
@Synchronized
@Synchronizedの役割ドメインは、メソッドの同期に使用されるメソッドです.この注釈を使用すると、メソッドボディのコードブロックがsynchronizeブロックに自動的に含まれます.synchronizeブロックロックのオブジェクトはクラスのメンバー属性に違いありません.@Synchronizedのvalue()で指定できます.存在しない場合はlombokで新規作成されます.一般的には
lombokの使用:
オリジナルJavaに相当:
@SneakyThrows
@SneakyThrowsの役割ドメインは、異常を自動的にキャプチャ(非表示)するための構築またはメソッドです.Javaは異常のチェックに対して,符号化時にキャプチャしたり,投げ出したりする必要があることを知っている.この注釈の役割は、検査異常を運転時異常として包装することであり、符号化時に異常を処理する必要はない.
ヒント:しかし、これは友好的な符号化方式ではありません.apiを作成したユーザーは、検査異常を処理する必要があることを明示的に知ることができません.
lombokの使用:
オリジナルJavaに相当:
@Log
@Logの役割ドメインはクラスであり,ログAPIハンドルを生成するために用いられる.現在サポートされているタイプは次のとおりです.
lombokの使用:
オリジナルJavaに相当:
小結
実は実現の角度から見ると、lombokは非常に奇抜な枠組みではなく、それが使っている技術はすでに「古い」Jdk 6の特性であるが、それが表現した特性は確かに奇技淫巧だと感じさせる.奇技で淫らなだけに、知らない人の多くは恐れて、システムに予知できないバグが発生する可能性が高いと思っています(ここでは突っ込んで、筆者の会社はlombokを無効にしています).実際,プラグイン化注釈処理APIに基づく生成コードはコンパイル中であり,何らかの問題が発生した場合,コンパイルが通過できないことは明らかである.lombokに問題があると思ったら、コンパイル後のファイルをめくって、異常があるかどうかを見ることができます.lombokは日常的な符号化で効率を大幅に向上させることができるので、百益無害に違いない.
(全文完了)
この文章は主にlombokの使用を紹介して、lombokのソースコードと原理についてはしばらく探究しないで、前の文章のプラグイン化注釈処理APIを見てlombokの基本原理を理解することができます.参考資料:
Project Lombokはjavaライブラリで、エディタに自動的に挿入し、javaをより豊かにするためのツールを構築します.もうgetterやequalsメソッドは使いません.valなど、将来のjavaプロパティにできるだけ早くアクセスします.これがlombokの公式プロフィールです(例えばJdk 9に追加されたvalキーワードはlombokに早く登場しました).lombokは実際にはJSR-269のプラグイン化注釈処理APIに基づいており、コンパイル中に特定の注釈を使用したクラス、メソッド、属性またはコードフラグメントを動的に修正し、カスタム機能を追加または実現するクラスライブラリである.
インストール
maven依存
プロジェクトでlombokに使用する注釈は、その依存性を導入する必要があります.
org.projectlombok
lombok
${version}
現在の最新バージョンは1.16.20です.
プラグイン
IDEにlombokのプラグインをインストールしないと、IDEはlombokコンパイル中に動的に生成されたコードを認識できず、コードブロックの赤として表現されるため、そのプラグインをインストールする必要があります.プラグインアドレス:https://projectlombok.org/download.
ダウンロードが完了すると、jarパッケージlombok-xx.xx.xx.jarが得られます.コマンドjava-jar lombok-xx.xx.xx.jarを直接使用して実行し、IDEA、eclipse、myeclipseにインストールします.
lombok注記
@val
@valは、修飾子がfinalのローカル変数タイプであることを宣言するために使用され、実際のタイプを記述する必要はありません.これは、コンパイラのタイプに依存します.@valの役割ドメインは局所変数である.例を次に示します.
lombokの使用:
import java.util.ArrayList;
import java.util.HashMap;
import lombok.val;
public class ValExample {
public String example() {
val example = new ArrayList();
example.add("Hello, World!");
val foo = example.get(0);
return foo.toLowerCase();
}
}
オリジナルJavaに相当:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
public class ValExample {
public String example() {
final ArrayList example = new ArrayList();
example.add("Hello, World!");
final String foo = example.get(0);
return foo.toLowerCase();
}
}
@var
@varの機能は@valに似ていますが、@varは非final修飾の局所変数を修飾します.
@NonNull
@NonNullの役割ドメインは、属性(field)、メソッドパラメータまたは、コンストラクション関数パラメータ、およびローカル変数(LOCAL_VARIABLE)であり、これらのパラメータに空のチェック文を追加します.基本フォーマットは:
if(param == null) {throw new NullPointerException("param ");}
です.lombokの使用:
import lombok.NonNull;
public class NonNullExample extends Something {
private String name;
public NonNullExample(@NonNull Person person) {
super("Hello");
this.name = person.getName();
}
}
オリジナルJavaに相当:
import lombok.NonNull;
public class NonNullExample extends Something {
private String name;
public NonNullExample(@NonNull Person person) {
super("Hello");
if (person == null) {
throw new NullPointerException("person");
}
this.name = person.getName();
}
}
@Cleanup
@Cleanupを使用すると、コード実行パスが現在の範囲を終了する前に、指定されたリソースが自動的にクリーンアップされることを確認できます.一般的には、ストリームのローカル変数のクローズを使用します.value()でリソースを閉じるメソッド名を指定できます.リソースを閉じるメソッドは、voidメソッドを参照しないでください.デフォルトのリソースを閉じるメソッド名はcloseです.
lombokの使用:
import lombok.Cleanup;
import java.io.*;
public class CleanupExample {
public static void main(String[] args) throws IOException {
@Cleanup InputStream in = new FileInputStream(args[0]);
@Cleanup OutputStream out = new FileOutputStream(args[1]);
byte[] b = new byte[10000];
while (true) {
int r = in.read(b);
if (r == -1) break;
out.write(b, 0, r);
}
}
}
オリジナルJavaに相当:
import java.io.*;
public class CleanupExample {
public static void main(String[] args) throws IOException {
InputStream in = new FileInputStream(args[0]);
try {
OutputStream out = new FileOutputStream(args[1]);
try {
byte[] b = new byte[10000];
while (true) {
int r = in.read(b);
if (r == -1) break;
out.write(b, 0, r);
}
} finally {
if (out != null) {
out.close();
}
}
} finally {
if (in != null) {
in.close();
}
}
}
}
@Getter/@Setter
@Getter/@Setter役割ドメインは属性またはクラスです.@Getterは指定された属性またはクラス内のすべての属性に対してGetterメソッドを生成し、@Setterは非final属性またはクラス内のすべての非final属性に対してSetterメソッドを生成することを指定します.生成方法の修飾子は@Getter/@Setterのvalue()のAccessLevel属性で指定でき、@Getterのブール値属性lazyで遅延ロードの有無を指定できます.
lombokの使用:
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
public class GetterSetterExample {
/**
* Age of the person. Water is wet.
*
* @param age New value for this person's age. Sky is blue.
* @return The current value of this person's age. Circles are round.
*/
@Getter @Setter private int age = 10;
/**
* Name of the person.
* -- SETTER --
* Changes the name of this person.
*
* @param name The new value.
*/
@Setter(AccessLevel.PROTECTED) private String name;
@Override public String toString() {
return String.format("%s (age: %d)", name, age);
}
}
オリジナルJavaに相当:
public class GetterSetterExample {
/**
* Age of the person. Water is wet.
*/
private int age = 10;
/**
* Name of the person.
*/
private String name;
@Override public String toString() {
return String.format("%s (age: %d)", name, age);
}
/**
* Age of the person. Water is wet.
*
* @return The current value of this person's age. Circles are round.
*/
public int getAge() {
return age;
}
/**
* Age of the person. Water is wet.
*
* @param age New value for this person's age. Sky is blue.
*/
public void setAge(int age) {
this.age = age;
}
/**
* Changes the name of this person.
*
* @param name The new value.
*/
protected void setName(String name) {
this.name = name;
}
}
@ToString
@ToStringの役割ドメインはクラスで、主な役割はクラス内のtoStringメソッドを上書きすることです.@ToStringの属性は比較的多く、以下のように紹介されています.
次は公式の例です.
lombokの使用:
import lombok.ToString;
@ToString(exclude="id")
public class ToStringExample {
private static final int STATIC_VAR = 10;
private String name;
private Shape shape = new Square(5, 10);
private String[] tags;
private int id;
public String getName() {
return this.name;
}
@ToString(callSuper=true, includeFieldNames=true)
public static class Square extends Shape {
private final int width, height;
public Square(int width, int height) {
this.width = width;
this.height = height;
}
}
}
オリジナルJavaに相当:
import java.util.Arrays;
public class ToStringExample {
private static final int STATIC_VAR = 10;
private String name;
private Shape shape = new Square(5, 10);
private String[] tags;
private int id;
public String getName() {
return this.getName();
}
public static class Square extends Shape {
private final int width, height;
public Square(int width, int height) {
this.width = width;
this.height = height;
}
@Override public String toString() {
return "Square(super=" + super.toString() + ", width=" + this.width + ", height=" + this.height + ")";
}
}
@Override public String toString() {
return "ToStringExample(" + this.getName() + ", " + this.shape + ", " + Arrays.deepToString(this.tags) + ")";
}
}
@EqualsAndHashCode
@EqualsAndHashCodeの役割ドメインはクラスであり、equals()メソッドとhashCode()メソッドを生成するために使用されます.この注記のプロパティは多いですが、@ToStringと似ています.例を次に示します.
lombokの使用:
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(exclude={"id", "shape"})
public class EqualsAndHashCodeExample {
private transient int transientVar = 10;
private String name;
private double score;
private Shape shape = new Square(5, 10);
private String[] tags;
private int id;
public String getName() {
return this.name;
}
@EqualsAndHashCode(callSuper=true)
public static class Square extends Shape {
private final int width, height;
public Square(int width, int height) {
this.width = width;
this.height = height;
}
}
}
オリジナルJavaに相当:
import java.util.Arrays;
public class EqualsAndHashCodeExample {
private transient int transientVar = 10;
private String name;
private double score;
private Shape shape = new Square(5, 10);
private String[] tags;
private int id;
public String getName() {
return this.name;
}
@Override public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof EqualsAndHashCodeExample)) return false;
EqualsAndHashCodeExample other = (EqualsAndHashCodeExample) o;
if (!other.canEqual((Object)this)) return false;
if (this.getName() == null ? other.getName() != null : !this.getName().equals(other.getName())) return false;
if (Double.compare(this.score, other.score) != 0) return false;
if (!Arrays.deepEquals(this.tags, other.tags)) return false;
return true;
}
@Override public int hashCode() {
final int PRIME = 59;
int result = 1;
final long temp1 = Double.doubleToLongBits(this.score);
result = (result*PRIME) + (this.name == null ? 43 : this.name.hashCode());
result = (result*PRIME) + (int)(temp1 ^ (temp1 >>> 32));
result = (result*PRIME) + Arrays.deepHashCode(this.tags);
return result;
}
protected boolean canEqual(Object other) {
return other instanceof EqualsAndHashCodeExample;
}
public static class Square extends Shape {
private final int width, height;
public Square(int width, int height) {
this.width = width;
this.height = height;
}
@Override public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof Square)) return false;
Square other = (Square) o;
if (!other.canEqual((Object)this)) return false;
if (!super.equals(o)) return false;
if (this.width != other.width) return false;
if (this.height != other.height) return false;
return true;
}
@Override public int hashCode() {
final int PRIME = 59;
int result = 1;
result = (result*PRIME) + super.hashCode();
result = (result*PRIME) + this.width;
result = (result*PRIME) + this.height;
return result;
}
protected boolean canEqual(Object other) {
return other instanceof Square;
}
}
}
@NoArgsConstructor, @RequiredArgsConstructor, @AllArgsConstructor
この3つの注釈の役割ドメインはすべてクラスです.@NoArgsConstructorの役割は、パラメータのない構造関数を生成することです.@AllArgsConstructorの役割は、すべてのフィールドを持つコンストラクション関数を生成することです.@RequiredArgsConstructorの役割はfinalまたは@NonNull修飾を使用した属性ごとに1つのパラメータしかない構造関数を生成することです.この3つの注記は、staticName()で構造の名前を指定し、access()で構造の修飾子を指定できます.@NoArgsConstructorの属性force()のデフォルト値はfalseであり、クラスにfinal属性が存在する場合に@NoArgsConstructorを使用するとコンパイルエラーが発生し、force()値がtrueの場合、final修飾属性は「パラメータなし構造」のパラメータとして使用され、属性は0、nullまたはfalseに割り当てられます.
lombokの使用:
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.AllArgsConstructor;
import lombok.NonNull;
@RequiredArgsConstructor(staticName = "of")
@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class ConstructorExample {
private int x, y;
@NonNull private T description;
@NoArgsConstructor
public static class NoArgsExample {
@NonNull private String field;
}
}
オリジナルJavaに相当:
public class ConstructorExample {
private int x, y;
@NonNull private T description;
private ConstructorExample(T description) {
if (description == null) throw new NullPointerException("description");
this.description = description;
}
public static ConstructorExample of(T description) {
return new ConstructorExample(description);
}
@java.beans.ConstructorProperties({"x", "y", "description"})
protected ConstructorExample(int x, int y, T description) {
if (description == null) throw new NullPointerException("description");
this.x = x;
this.y = y;
this.description = description;
}
public static class NoArgsExample {
@NonNull private String field;
public NoArgsExample() {
}
}
}
@Data
@Dataの役割ドメインはクラスであり,@Getter,@Setter,@ToString,@EqualsAndHashCode,@RequiredArgsConstructorの同時適用に相当する.カスタムコンストラクション関数がすでに表示されている場合は、コンストラクション関数は自動的に生成されません.例を次に示します.
lombokの使用:
import lombok.AccessLevel;
import lombok.Setter;
import lombok.Data;
import lombok.ToString;
@Data public class DataExample {
private final String name;
@Setter(AccessLevel.PACKAGE) private int age;
private double score;
private String[] tags;
@ToString(includeFieldNames=true)
@Data(staticConstructor="of")
public static class Exercise {
private final String name;
private final T value;
}
}
オリジナルJavaに相当:
import java.util.Arrays;
public class DataExample {
private final String name;
private int age;
private double score;
private String[] tags;
public DataExample(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
void setAge(int age) {
this.age = age;
}
public int getAge() {
return this.age;
}
public void setScore(double score) {
this.score = score;
}
public double getScore() {
return this.score;
}
public String[] getTags() {
return this.tags;
}
public void setTags(String[] tags) {
this.tags = tags;
}
@Override public String toString() {
return "DataExample(" + this.getName() + ", " + this.getAge() + ", " + this.getScore() + ", " + Arrays.deepToString(this.getTags()) + ")";
}
protected boolean canEqual(Object other) {
return other instanceof DataExample;
}
@Override public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof DataExample)) return false;
DataExample other = (DataExample) o;
if (!other.canEqual((Object)this)) return false;
if (this.getName() == null ? other.getName() != null : !this.getName().equals(other.getName())) return false;
if (this.getAge() != other.getAge()) return false;
if (Double.compare(this.getScore(), other.getScore()) != 0) return false;
if (!Arrays.deepEquals(this.getTags(), other.getTags())) return false;
return true;
}
@Override public int hashCode() {
final int PRIME = 59;
int result = 1;
final long temp1 = Double.doubleToLongBits(this.getScore());
result = (result*PRIME) + (this.getName() == null ? 43 : this.getName().hashCode());
result = (result*PRIME) + this.getAge();
result = (result*PRIME) + (int)(temp1 ^ (temp1 >>> 32));
result = (result*PRIME) + Arrays.deepHashCode(this.getTags());
return result;
}
public static class Exercise {
private final String name;
private final T value;
private Exercise(String name, T value) {
this.name = name;
this.value = value;
}
public static Exercise of(String name, T value) {
return new Exercise(name, value);
}
public String getName() {
return this.name;
}
public T getValue() {
return this.value;
}
@Override public String toString() {
return "Exercise(name=" + this.getName() + ", value=" + this.getValue() + ")";
}
protected boolean canEqual(Object other) {
return other instanceof Exercise;
}
@Override public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof Exercise)) return false;
Exercise> other = (Exercise>) o;
if (!other.canEqual((Object)this)) return false;
if (this.getName() == null ? other.getValue() != null : !this.getName().equals(other.getName())) return false;
if (this.getValue() == null ? other.getValue() != null : !this.getValue().equals(other.getValue())) return false;
return true;
}
@Override public int hashCode() {
final int PRIME = 59;
int result = 1;
result = (result*PRIME) + (this.getName() == null ? 43 : this.getName().hashCode());
result = (result*PRIME) + (this.getValue() == null ? 43 : this.getValue().hashCode());
return result;
}
}
}
@Value
@Valueの役割ドメインはクラスであり、@Dataと似ていますが、可変タイプに使用されます.生成されたクラスとすべてのフィールドがfinalに設定され、すべてのフィールドがprivateで、自動的にGetterが生成されますが、Setterがなく、すべてのフィールドを初期化するコンストラクション関数が生成されます.final,@ToString,@EqualsAndHashCode,@AllArgsConstructor,@FieldDefaults(makeFinal=true,level=AccessLevel.PRIVATE)と@Getterを同時に適用したものに相当する.
lombokの使用:
import lombok.AccessLevel;
import lombok.experimental.NonFinal;
import lombok.experimental.Value;
import lombok.experimental.Wither;
import lombok.ToString;
@Value public class ValueExample {
String name;
@Wither(AccessLevel.PACKAGE) @NonFinal int age;
double score;
protected String[] tags;
@ToString(includeFieldNames=true)
@Value(staticConstructor="of")
public static class Exercise {
String name;
T value;
}
}
オリジナルJavaに相当:
import java.util.Arrays;
public final class ValueExample {
private final String name;
private int age;
private final double score;
protected final String[] tags;
@java.beans.ConstructorProperties({"name", "age", "score", "tags"})
public ValueExample(String name, int age, double score, String[] tags) {
this.name = name;
this.age = age;
this.score = score;
this.tags = tags;
}
public String getName() {
return this.name;
}
public int getAge() {
return this.age;
}
public double getScore() {
return this.score;
}
public String[] getTags() {
return this.tags;
}
@java.lang.Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof ValueExample)) return false;
final ValueExample other = (ValueExample)o;
final Object this$name = this.getName();
final Object other$name = other.getName();
if (this$name == null ? other$name != null : !this$name.equals(other$name)) return false;
if (this.getAge() != other.getAge()) return false;
if (Double.compare(this.getScore(), other.getScore()) != 0) return false;
if (!Arrays.deepEquals(this.getTags(), other.getTags())) return false;
return true;
}
@java.lang.Override
public int hashCode() {
final int PRIME = 59;
int result = 1;
final Object $name = this.getName();
result = result * PRIME + ($name == null ? 43 : $name.hashCode());
result = result * PRIME + this.getAge();
final long $score = Double.doubleToLongBits(this.getScore());
result = result * PRIME + (int)($score >>> 32 ^ $score);
result = result * PRIME + Arrays.deepHashCode(this.getTags());
return result;
}
@java.lang.Override
public String toString() {
return "ValueExample(name=" + getName() + ", age=" + getAge() + ", score=" + getScore() + ", tags=" + Arrays.deepToString(getTags()) + ")";
}
ValueExample withAge(int age) {
return this.age == age ? this : new ValueExample(name, age, score, tags);
}
public static final class Exercise {
private final String name;
private final T value;
private Exercise(String name, T value) {
this.name = name;
this.value = value;
}
public static Exercise of(String name, T value) {
return new Exercise(name, value);
}
public String getName() {
return this.name;
}
public T getValue() {
return this.value;
}
@java.lang.Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof ValueExample.Exercise)) return false;
final Exercise> other = (Exercise>)o;
final Object this$name = this.getName();
final Object other$name = other.getName();
if (this$name == null ? other$name != null : !this$name.equals(other$name)) return false;
final Object this$value = this.getValue();
final Object other$value = other.getValue();
if (this$value == null ? other$value != null : !this$value.equals(other$value)) return false;
return true;
}
@java.lang.Override
public int hashCode() {
final int PRIME = 59;
int result = 1;
final Object $name = this.getName();
result = result * PRIME + ($name == null ? 43 : $name.hashCode());
final Object $value = this.getValue();
result = result * PRIME + ($value == null ? 43 : $value.hashCode());
return result;
}
@java.lang.Override
public String toString() {
return "ValueExample.Exercise(name=" + getName() + ", value=" + getValue() + ")";
}
}
}
@Builder
@Builderの役割ドメインはクラスです.この注釈を使用してクラスにメンバークラス(Builder)を追加すると、ビルダーモードが使用され、コンパイル時にBuilder内部クラスとフィールド全体のコンストラクタが追加されます[email protected]はBuilderのプロパティのデフォルト値を指定するために使用されます.@Singularはlombokの現在のプロパティタイプがセットタイプであることを示します.lombokは信頼できるセットタイプにsetterメソッドではなくadderメソッドを追加します.
lombokの使用:
import lombok.Builder;
import lombok.Singular;
import java.util.Set;
@Builder
public class BuilderExample {
@Builder.Default private long created = System.currentTimeMillis();
private String name;
private int age;
@Singular private Set occupations;
}
オリジナルJavaに相当:
import java.util.Set;
public class BuilderExample {
private long created;
private String name;
private int age;
private Set occupations;
BuilderExample(String name, int age, Set occupations) {
this.name = name;
this.age = age;
this.occupations = occupations;
}
private static long $default$created() {
return System.currentTimeMillis();
}
public static BuilderExampleBuilder builder() {
return new BuilderExampleBuilder();
}
public static class BuilderExampleBuilder {
private long created;
private boolean created$set;
private String name;
private int age;
private java.util.ArrayList occupations;
BuilderExampleBuilder() {
}
public BuilderExampleBuilder created(long created) {
this.created = created;
this.created$set = true;
return this;
}
public BuilderExampleBuilder name(String name) {
this.name = name;
return this;
}
public BuilderExampleBuilder age(int age) {
this.age = age;
return this;
}
public BuilderExampleBuilder occupation(String occupation) {
if (this.occupations == null) {
this.occupations = new java.util.ArrayList();
}
this.occupations.add(occupation);
return this;
}
public BuilderExampleBuilder occupations(Collection extends String> occupations) {
if (this.occupations == null) {
this.occupations = new java.util.ArrayList();
}
this.occupations.addAll(occupations);
return this;
}
public BuilderExampleBuilder clearOccupations() {
if (this.occupations != null) {
this.occupations.clear();
}
return this;
}
public BuilderExample build() {
// complicated switch statement to produce a compact properly sized immutable set omitted.
Set occupations = ...;
return new BuilderExample(created$set ? created : BuilderExample.$default$created(), name, age, occupations);
}
@java.lang.Override
public String toString() {
return "BuilderExample.BuilderExampleBuilder(created = " + this.created + ", name = " + this.name + ", age = " + this.age + ", occupations = " + this.occupations + ")";
}
}
}
@Synchronized
@Synchronizedの役割ドメインは、メソッドの同期に使用されるメソッドです.この注釈を使用すると、メソッドボディのコードブロックがsynchronizeブロックに自動的に含まれます.synchronizeブロックロックのオブジェクトはクラスのメンバー属性に違いありません.@Synchronizedのvalue()で指定できます.存在しない場合はlombokで新規作成されます.一般的には
private final Object $lock = new Object[0];
です.lombokの使用:
import lombok.Synchronized;
public class SynchronizedExample {
private final Object readLock = new Object();
@Synchronized
public static void hello() {
System.out.println("world");
}
@Synchronized
public int answerToLife() {
return 42;
}
@Synchronized("readLock")
public void foo() {
System.out.println("bar");
}
}
オリジナルJavaに相当:
public class SynchronizedExample {
private static final Object $LOCK = new Object[0];
private final Object $lock = new Object[0];
private final Object readLock = new Object();
public static void hello() {
synchronized($LOCK) {
System.out.println("world");
}
}
public int answerToLife() {
synchronized($lock) {
return 42;
}
}
public void foo() {
synchronized(readLock) {
System.out.println("bar");
}
}
}
@SneakyThrows
@SneakyThrowsの役割ドメインは、異常を自動的にキャプチャ(非表示)するための構築またはメソッドです.Javaは異常のチェックに対して,符号化時にキャプチャしたり,投げ出したりする必要があることを知っている.この注釈の役割は、検査異常を運転時異常として包装することであり、符号化時に異常を処理する必要はない.
ヒント:しかし、これは友好的な符号化方式ではありません.apiを作成したユーザーは、検査異常を処理する必要があることを明示的に知ることができません.
lombokの使用:
import lombok.SneakyThrows;
public class SneakyThrowsExample implements Runnable {
@SneakyThrows(UnsupportedEncodingException.class)
public String utf8ToString(byte[] bytes) {
return new String(bytes, "UTF-8");
}
@SneakyThrows
public void run() {
throw new Throwable();
}
}
オリジナルJavaに相当:
import lombok.Lombok;
public class SneakyThrowsExample implements Runnable {
public String utf8ToString(byte[] bytes) {
try {
return new String(bytes, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw Lombok.sneakyThrow(e);
}
}
public void run() {
try {
throw new Throwable();
} catch (Throwable t) {
throw Lombok.sneakyThrow(t);
}
}
}
@Log
@Logの役割ドメインはクラスであり,ログAPIハンドルを生成するために用いられる.現在サポートされているタイプは次のとおりです.
@CommonsLog
:private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(LogExample.class);
@JBossLog
:private static final org.jboss.logging.Logger log = org.jboss.logging.Logger.getLogger(LogExample.class);
@Log
:private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(LogExample.class.getName());
@Log4j
:private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(LogExample.class);
@Log4j2
:private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class);
@Slf4j
:private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExample.class);
@XSlf4j
:private static final org.slf4j.ext.XLogger log = org.slf4j.ext.XLoggerFactory.getXLogger(LogExample.class);
lombokの使用:
import lombok.extern.java.Log;
import lombok.extern.slf4j.Slf4j;
@Log
public class LogExample {
public static void main(String... args) {
log.error("Something's wrong here");
}
}
@Slf4j
public class LogExampleOther {
public static void main(String... args) {
log.error("Something else is wrong here");
}
}
@CommonsLog(topic="CounterLog")
public class LogExampleCategory {
public static void main(String... args) {
log.error("Calling the 'CounterLog' with a message");
}
}
オリジナルJavaに相当:
public class LogExample {
private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(LogExample.class.getName());
public static void main(String... args) {
log.error("Something's wrong here");
}
}
public class LogExampleOther {
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExampleOther.class);
public static void main(String... args) {
log.error("Something else is wrong here");
}
}
public class LogExampleCategory {
private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog("CounterLog");
public static void main(String... args) {
log.error("Calling the 'CounterLog' with a message");
}
}
小結
実は実現の角度から見ると、lombokは非常に奇抜な枠組みではなく、それが使っている技術はすでに「古い」Jdk 6の特性であるが、それが表現した特性は確かに奇技淫巧だと感じさせる.奇技で淫らなだけに、知らない人の多くは恐れて、システムに予知できないバグが発生する可能性が高いと思っています(ここでは突っ込んで、筆者の会社はlombokを無効にしています).実際,プラグイン化注釈処理APIに基づく生成コードはコンパイル中であり,何らかの問題が発生した場合,コンパイルが通過できないことは明らかである.lombokに問題があると思ったら、コンパイル後のファイルをめくって、異常があるかどうかを見ることができます.lombokは日常的な符号化で効率を大幅に向上させることができるので、百益無害に違いない.
(全文完了)