Lombok

は、私が文字通り常に自分のプロジェクトに落とし込むツールの1つです。私は最近、それなしでJavaをプログラミングすることを想像することができませんでした。あなたがこの記事を読んでその力を見つけられることを本当に願っています!


1繰り返しコードを避ける

Javaは素晴らしい言語ですが、一般的なタスクやいくつかのフレームワークプラクティスに準拠するためにコードで行う必要があることに対して、冗長すぎることがあります。これらはあなたのプログラムのビジネス面に本当の価値をもたらさないことがよくあります – そしてこれがロンボクがあなたの人生をより幸せにそしてあなた自身をより生産的にするためにここにあるところです。

それが機能する方法は、あなたのビルドプロセスにプラグインして、あなたがあなたのコードで導入する多くのプロジェクトアノテーションに従ってあなたの

.class

ファイルに自動生成するJavaバイトコードです。

どちらのシステムを使用している場合でも、ビルドに含めることは非常に簡単です。彼らのhttps://projectlombok.org/features/index.html[project page]には、詳細に関する詳細な説明があります。私のプロジェクトの大部分はMavenベースであるため、私は通常、それらの依存関係を

provided

スコープに落とすだけです。

<dependencies>
    ...
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.16.8</version>
        <scope>provided</scope>
    </dependency>
    ...
</dependencies>

最新バージョンhttps://projectlombok.org/changelog.html[ここ]を確認してください。

Lombokによっては、ランタイムではなく純粋なビルドの依存関係であるため、あなたの


jar __ sのユーザーもそれに依存するようにはなりません。


2ゲッター/セッター、コンストラクタ – とても反復的

パブリックなgetterメソッドおよびsetterメソッドを介してオブジェクトのプロパティをカプセル化することは、Javaの世界ではそのような一般的な方法です。

これは非常に一般的なことなので、ほとんどのIDEはこれらのパターンの自動生成コードをサポートしています。しかし、このコードはあなたのソースに存在する必要があり、例えば新しいプロパティが追加されたときやフィールドの名前が変更されたときにも維持される必要があります。

例としてJPAエンティティとして使用したいこのクラスを考えてみましょう。

@Entity
public class User implements Serializable {

    private @Id Long id;//will be set when persisting

    private String firstName;
    private String lastName;
    private int age;

    public User() {
    }

    public User(String firstName, String lastName, int age) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
    }

   //getters and setters: ~30 extra lines of code
}

これはかなり単純なクラスですが、ゲッターとセッターのための追加コードを追加した場合でも、関連するビジネス情報よりも定型的なゼロ値コードを使用するという定義になります。姓、年齢。」

このクラスを「ロンボク化」しましょう:

@Entity
@Getter @Setter @NoArgsConstructor//<--- THIS is it
public class User implements Serializable {

    private @Id Long id;//will be set when persisting

    private String firstName;
    private String lastName;
    private int age;

    public User(String firstName, String lastName, int age) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
    }
}

Lombokに

@ Getter

および

@ Setter

アノテーションを追加して、クラスのすべてのフィールドに対してこれらを生成するようにしました。


@ NoArgsConstructor

は空のコンストラクタを生成します。

これは

whole

クラスのコードであることに注意してください。私は

//gettersとsetters

のコメントを付けた上記のバージョンとは対照的に何も省略していません。

3つの関連属性クラスの場合、これはコードの大幅な節約になります。


User

クラスにさらに属性(プロパティ)を追加しても、同じことが起こります。注釈を型自体に適用したので、デフォルトですべてのフィールドが考慮されます。

あなたがいくつかのプロパティの可視性を洗練させたい場合はどうなりますか?たとえば、エンティティの

id

フィールド修飾子

package

または

protected

は、読み取りが想定されているがアプリケーションコードによって明示的に設定されていないため、表示されたままにしておくのが好きです。この特定のフィールドには、よりきめの細かい

@ Setter

を使用するだけです。

private @Id @Setter(AccessLevel.PROTECTED) Long id;


3バリュークラス/DTOの

複雑な「値」または「データ転送オブジェクト」として表現することを唯一の目的としてデータ型を定義したいという状況はたくさんあります。 。

成功したログイン操作を表すクラスを設計します。すべてのフィールドをnull以外にし、オブジェクトを不変にして、スレッドセーフでそのプロパティにアクセスできるようにします。

public class LoginResult {

    private final Instant loginTs;

    private final String authToken;
    private final Duration tokenValidity;

    private final URL tokenRefreshUrl;

   //constructor taking every field and checking nulls

   //read-only accessor, not necessarily as get** () form
}

繰り返しますが、コメント付きセクションのために書かなければならないコードの量は、私たちがカプセル化したい情報であり、それが私たちにとって本当に価値のあるものである、はるかに大量のものになるでしょう。これを改善するために、Lombokをもう一度使用することができます。

@RequiredArgsConstructor
@Accessors(fluent = true) @Getter
public class LoginResult {

    private final @NonNull Instant loginTs;

    private final @NonNull String authToken;
    private final @NonNull Duration tokenValidity;

    private final @NonNull URL tokenRefreshUrl;

}


@ RequiredArgsConstructor

アノテーションを追加するだけで、宣言したとおりに、クラス内のすべての最後のフィールドのコンストラクタが得られます。属性に

@ NonNull

を追加すると、コンストラクターはNULL可能性をチェックし、それに応じて

NullPointerExceptions

をスローします。これは、フィールドが非最終フィールドで、それらに

@ Setter

を追加した場合にも発生します。

あなたの財産のために古い

get ** ()

フォームを退屈させたくないですか?この例では

@ Accessors(fluent = true)

を追加したため、「getters」はプロパティと同じメソッド名になります。

getAuthToken()

は単に

authToken()

になります。

この「流暢な」形式は、属性設定者の非最終フィールドに適用され、連鎖呼び出しも可能になります。

----//Imagine fields were no longer final now
return new LoginResult()
  .loginTs(Instant.now())
  .authToken("asdasd")
  .//and so on
----


4コアJava定型文

私たちが維持する必要があるコードを書くことになってしまうもう一つの状況は、

toString()



equals()

および

hashCode()

メソッドを生成するときです。 IDEはクラス属性の観点からこれらを自動生成するためのテンプレートを手助けしようとします。

他のLombokクラスレベルのアノテーションによってこれを自動化することができます。

すべてのクラス属性を含む

toString()

メソッドを生成します。データモデルが充実しているので、自分で書いて保守する必要はありません。


http://www.artima.com/lejava/articles/equality.htmlによると、デフォルトで


equals()

メソッドと

hashCode()

メソッドの両方がデフォルトで生成されます。

これらのジェネレータは非常に便利な設定オプションを出荷します。たとえば、注釈付きクラスが階層の一部になっている場合は、

callSuper = true

パラメータを使用するだけで、メソッドのコードを生成するときに親の結果が考慮されます。

さらに詳しく説明すると、

User

JPAエンティティの例に、このユーザーに関連するイベントへの参照が含まれているとします。

@OneToMany(mappedBy = "user")
private List<UserEvent> events;


@ ToString

アノテーションを使用したからといって、ユーザーの

toString()

メソッドを呼び出すたびにイベントのホールリストをダンプしたくないでしょう。問題ありません。このようにパラメータ化するだけです。


@ ToString(exclude = \ {“ events”})

でも、それは起こりません。これは、たとえば

__UserEvent


sに

User__への参照がある場合に循環参照を回避するのにも役立ちます。


LoginResult

の例では、クラス内の他の最終属性ではなく、トークン自体の観点から等価性とハッシュコードの計算を定義することができます。それから、単に

@ EqualsAndHashCode(of = \ {“ authToken”})

のようなものを書くだけです。

ボーナス:これまでにレビューした注釈の機能が気に入った場合は、https://projectlombok.org/features/Data.html[

@Data

]およびhttps://projectlombok.org/features/を調べてください。 Value.html[

@ Value

]アノテーションは、それらが一連のクラスに適用されたかのように動作します。結局のところ、これらの議論された用法は非常に一般的に多くの場合まとめられています。


5ビルダーパターン

以下は、REST APIクライアント用のサンプル構成クラスになります。

public class ApiClientConfiguration {

    private String host;
    private int port;
    private boolean useHttps;

    private long connectTimeout;
    private long readTimeout;

    private String username;
    private String password;

   //Whatever other options you may thing.

   //Empty constructor? All combinations?

   //getters... and setters?
}

クラスのデフォルトの空のコンストラクタを使用し、すべてのフィールドに設定メソッドを提供することに基づいて、最初のアプローチをとることができます。ただし、構築(インスタンス化)した後は、構成を

set

-edしないようにして、事実上不変にすることが理想的です。したがって、セッターを避けたいのですが、そのような潜在的に長いargsコンストラクターを書くことはアンチパターンです。

代わりに、

Builderアノテーションを単に

ApiClientConfiguration

に追加することで、

builder

パターンを生成し、余分な

Builder__クラスとそれに関連する流暢なセッター風のメソッドを記述しないようにすることができます。

@Builder
public class ApiClientConfiguration {

   //... everything else remains the same

}

上記のクラス定義をそのまま(コンストラクタもセッターも宣言せず

@ Builder

)にしておくと、次のように使用できます。

ApiClientConfiguration config =
    new ApiClientConfigurationBuilder()
        .host("api.server.com")
        .port(443)
        .useHttps(true)
        .connectTimeout(15__000L)
        .readTimeout(5__000L)
        .username("myusername")
        .password("secret")
    .build();

** 6. チェック済み例外

多くのJava APIは、クライアントコードが

catch

を宣言するか

throws

を宣言することを強いられる、いくつかのチェックされた例外を投げることができるように設計されています。これらの例外を何度もこのようなものにすることはできないとわかっていますか。

public String resourceAsString() {
    try (InputStream is = this.getClass().getResourceAsStream("sure__in__my__jar.txt")) {
        BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
        return br.lines().collect(Collectors.joining("\n"));
    } catch (IOException | UnsupportedCharsetException ex) {
       //If this ever happens, then its a bug.
        throw new RuntimeException(ex); <--- encapsulate into a Runtime ex.
    }
}

このコードパターンを避けたいのであれば、コンパイラはそれ以外では幸せではないでしょう(そして、結局のところ、チェックエラーが発生し得ないことを知っているならば)。 html[

@ SneakyThrows

]:

@SneakyThrows
public String resourceAsString() {
    try (InputStream is = this.getClass().getResourceAsStream("sure__in__my__jar.txt")) {
        BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
        return br.lines().collect(Collectors.joining("\n"));
    }
}


7. あなたのリソースが解放されていることを確認する

Java 7では、

java.lang

.

AutoCloseable

を実装しているものすべてのインスタンスによって保持されているリソースが終了時に解放されるように、try-with-resourcesブロックが導入されました。

ロンボクはこれを達成するための代替手段を、そしてより柔軟にhttps://projectlombok.org/features/Cleanup.html[@Cleanup]を通して提供します。確認したいリソースが解放されているローカル変数に使用します。特定のインターフェースを実装する必要はありません。その

close()

メソッドを呼び出すだけです。

@Cleanup InputStream is = this.getClass().getResourceAsStream("res.txt");

あなたのリリース方法は違う名前ですか?問題はありません。アノテーションをカスタマイズするだけです。

@Cleanup("dispose") JFrame mainFrame = new JFrame("Main Window");


8ロガーを取得するためにあなたのクラスに注釈を付ける

私たちの多くは、選択したフレームワークから

Logger

のインスタンスを作成することによって、ログステートメントをコードに慎重に追加します。言う、SLF4J:

public class ApiClientConfiguration {

    private static Logger LOG = LoggerFactory.getLogger(ApiClientConfiguration.class);

   //LOG.debug(), LOG.info(), ...

}

これは非常に一般的なパターンであり、Lombokの開発者はこれを単純化することに気を配っています。

@Slf4j//or: @Log @CommonsLog @Log4j @Log4j2 @XSlf4j
public class ApiClientConfiguration {

   //log.debug(), log.info(), ...

}

多くのhttps://projectlombok.org/features/Log.html[ロギングフレームワーク]がサポートされており、もちろんインスタンス名、トピックなどをカスタマイズできます。


9スレッドセーフなメソッドを書く

Javaでは、

synchronized

キーワードを使用して重要なセクションを実装できます。ただし、これは100%安全な方法ではありません。他のクライアントコードも最終的にインスタンスと同期する可能性があり、予期しないデッドロックを引き起こす可能性があります。

これがhttps://projectlombok.org/features/Synchronized.html[

@Synchronized

]が入ってくる場所です:あなたのメソッド(インスタンスと静的の両方)にそれに注釈を付けると、あなたの実装が使うことになる自動生成された非公開フィールドを得るでしょう。ロック用:

@Synchronized
public/**  better than: synchronized ** /void putValueInCache(String key, Object value) {
   //whatever here will be thread-safe code
}


10自動オブジェクトコンポジション

Javaには、「コンポジション継承を優先する」アプローチをスムーズにするための言語レベルの構成要素はありません。他の言語には、これを実現するための

Traits



Mixins

などの概念が組み込まれています。

このプログラミングパターンを使用したい場合は、Lombokのhttps://projectlombok.org/features/experimental/Delegate.html[@Delegate]が非常に便利です。例を考えましょう。


  • __User


    sと


    Customer

    __sがいくつかの共通の属性を共有したい

命名および電話番号
** これらのフィールドにはインターフェースとアダプタクラスの両方を定義しています。

  • 私達は私達のモデルにインターフェースを実装させ、それらに

    @ Delegate

    をさせます。

アダプター、連絡先情報を使用して効果的にそれらを構成する

まず、インターフェースを定義しましょう。

public interface HasContactInformation {

    String getFirstName();
    void setFirstName(String firstName);

    String getFullName();

    String getLastName();
    void setLastName(String lastName);

    String getPhoneNr();
    void setPhoneNr(String phoneNr);

}

そして今度は

support

クラスとしてのアダプタ:

@Data
public class ContactInformationSupport implements HasContactInformation {

    private String firstName;
    private String lastName;
    private String phoneNr;

    @Override
    public String getFullName() {
        return getFirstName() + " " + getLastName();
    }
}

興味深い部分は、連絡先情報を両方のモデルクラスに簡単に作成できるようになったことです。

public class User implements HasContactInformation {

   //Whichever other User-specific attributes

    @Delegate(types = {HasContactInformation.class})
    private final ContactInformationSupport contactInformation =
            new ContactInformationSupport();

   //User itself will implement all contact information by delegation

}


Customer

の場合も非常に似ていますが、簡潔にするためにサンプルは省略します。


11 Rolling Lombok Back?

短い答え:まったく違います。

あなたは自分のプロジェクトの1つでLombokを使う可能性があることを心配しているかもしれませんが、後でその決定をロールバックしたいと思います。それであなたはそれのために注釈を付けられた多分多数のクラスを持っているでしょう…あなたは何ができるでしょうか?

私はこれを本当に後悔したことはありませんが、あなた、あなたのチーム、またはあなたの組織を知っている人は誰ですか。このような場合は、同じプロジェクトの

delombok

ツールを使用して説明します。

あなたのコードを

delombok-ingすることで、あなたはLombokが構築したバイトコードと全く同じ機能を持つ自動生成されたJavaソースコードを得るでしょう。それで、あなたは単にあなたのオリジナルの注釈を付けられたコードをこれらの新しい

delomboked__ファイルに取り替えることができて、もはやそれに依存しません。

これはあなたができることですhttps://projectlombok.org/features/delombok.html[あなたのビルドに統合]そして私はこれまでに生成されたコードを研究するか、または他のJavaソースコードベースのツールとLombokを統合するためにこれを行いました。


12. 結論

この記事では紹介していない機能が他にもいくつかあります。詳細と使用例については、https://projectlombok.org/features/index.html[機能の概要]を参照してください。

また、私たちが示したほとんどの関数には、命名などのためにあなたのチームのプラクティスに最も準拠したものを生成するために便利なカスタマイズオプションがいくつかあります。 .html[設定システム]もそれを助けてくれるでしょう。

あなたがLombokにあなたのJava開発ツールセットに入る機会を与える動機を見つけたことを願っています。試してみて、生産性を高めましょう。

サンプルコードはhttps://github.com/eugenp/tutorials/tree/master/lombok[GitHubプロジェクト]にあります。