1. 序章

Project Lombok は、ソースコード内の反復タスクに対するJavaの冗長性を減らすのに役立ちます。 このチュートリアルでは、Lombokでローカルval変数とvar変数を宣言して型を推測する方法を説明します。

2. Lombokでのvalおよびvar変数の宣言

Lombokは、ボイラープレートコードを回避するためのインテリジェントな機能を提供します。 たとえば、ドメインモデルオブジェクトからゲッターとセッターを非表示にします。 Builder アノテーションは、Builderパターンを適切に実装するのに役立つもう1つの興味深い機能です。

次のセクションでは、タイプを指定せずにローカル変数を定義するLombok機能に焦点を当てます。 Lombok valおよびvarタイプを使用して変数を宣言し、ソースコードの余分な行を回避します。

val はバージョン0.10で導入されました。 val を使用する場合、Lombokは変数を final として宣言し、初期化後にタイプを自動的に推測します。 したがって、初期化式は必須です。

varはバージョン1.16.20で導入されました。 val と同様に、変数が final として宣言されていないという大きな違いはありますが、初期化式から型を推測します。 したがって、それ以上の割り当ては許可されますが、変数を宣言するときに指定されたタイプに準拠する必要があります。

3. valおよびvarの例をLombokに実装する

3.1. 依存関係

例を実装するには、Lombok依存関係をpom.xmlに追加するだけです。

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

最新の利用可能なバージョンここを確認できます。

3.2. val変数宣言

まず、valタイプをLombokからインポートします。

import lombok.val;

次に、valを使用してさまざまなローカル変数を宣言します。 たとえば、単純なStringから始めることができます。

public Class name() {
    val name = "name";
    System.out.println("Name: " + name);
    return name.getClass();
}

Lombokは、次のバニラJavaを自動的に生成します。

final java.lang.String name = "name";

次に、Integerを作成しましょう。

public Class age() {
    val age = Integer.valueOf(30);
    System.out.println("Age: " + age);
    return age.getClass();
}

ご覧のとおり、Lombokは適切なタイプを生成します。

final java.lang.Integer age = Integer.valueOf(30);

リストを宣言することもできます。

public Class listOf() {
    val agenda = new ArrayList<String>();
    agenda.add("Day 1");
    System.out.println("Agenda: " + agenda);
    return agenda.getClass();
}

Lombokは、 List だけでなく、その中のタイプも推測します。

final java.util.ArrayList<java.lang.String> agenda = new ArrayList<String>();

それでは、マップを作成しましょう。

public Class mapOf() {
    val books = new HashMap<Integer, String>();
    books.put(1, "Book 1");
    books.put(2, "Book 2");
    System.out.println("Books:");
    for (val entry : books.entrySet()) {
        System.out.printf("- %d. %s\n", entry.getKey(), entry.getValue());
    }
    return books.getClass();
}

ここでも、適切なタイプが推測されます。

final java.util.HashMap<java.lang.Integer, java.lang.String> books = new HashMap<Integer, String>();
// ...
for (final java.util.Map.Entry<java.lang.Integer, java.lang.String> entry : books.entrySet()) {
   // ...
}

Lombokが適切な型をfinalとして宣言していることがわかります。 したがって、名前を変更しようとすると、 val の最終的な性質により、ビルドは失敗します。

name = "newName";

[12,9] cannot assign a value to final variable name

次に、いくつかのテストを実行して、Lombokが適切なタイプを生成することを確認します。

ValExample val = new ValExample();
assertThat(val.name()).isEqualTo(String.class);
assertThat(val.age()).isEqualTo(Integer.class);
assertThat(val.listOf()).isEqualTo(ArrayList.class);
assertThat(val.mapOf()).isEqualTo(HashMap.class);

最後に、コンソール出力で特定のタイプのオブジェクトを確認できます。

Name: name
Age: 30
Agenda: [Day 1]
Books:
- 1. Book 1
- 2. Book 2

3.3. var変数宣言

var宣言はvalと非常によく似ていますが、変数がfinalではないという特殊性があります。

import lombok.var;

var name = "name";
name = "newName";

var age = Integer.valueOf(30);
age = 35;

var agenda = new ArrayList<String>();
agenda.add("Day 1");
agenda = new ArrayList<String>(Arrays.asList("Day 2"));

var books = new HashMap<Integer, String>();
books.put(1, "Book 1");
books.put(2, "Book 2");
books = new HashMap<Integer, String>();
books.put(3, "Book 3");
books.put(4, "Book 4");

生成されたバニラJavaを見てみましょう。

var name = "name";

var age = Integer.valueOf(30);

var agenda = new ArrayList<String>();

var books = new HashMap<Integer, String>();

これは、Java10がvar宣言をサポートして初期化式を使用してローカル変数のタイプを推測するためです。 ただし、使用する際には、いくつかの制約を考慮する必要があります。

宣言された変数はfinalではないため、さらに割り当てを行うことができます。 それでも、オブジェクトは、初期化式から推測された適切な型に適合している必要があります。

別のタイプを割り当てようとすると、コンパイル中にエラーが発生します。

books = new ArrayList<String>();

[37,17] incompatible types: java.util.ArrayList<java.lang.String> cannot be converted to java.util.HashMap<java.lang.Integer,java.lang.String>

テストを少し変更して、新しい割り当てを確認しましょう。

VarExample varExample = new VarExample();
assertThat(varExample.name()).isEqualTo("newName");
assertThat(varExample.age()).isEqualTo(35);
assertThat("Day 2").isIn(varExample.listOf());
assertThat(varExample.mapOf()).containsValue("Book 3");

そして最後に、コンソール出力も前のセクションとは異なります。

Name: newName
Age: 35
Agenda: [Day 2]
Books:
- 3. Book 3
- 4. Book 4

4. 化合物タイプ

イニシャライザ式として複合型を使用する必要がある場合があります。

val compound = isArray ? new ArrayList<String>() : new HashSet<String>();

上記のスニペットでは、割り当てはブール値 に依存し、最も一般的なスーパークラスが推測されます。

Lombokは、バニラコードが示すように、タイプとしてAbstractCollectionを割り当てます。

final java.util.AbstractCollection<java.lang.String> compound = isArray ? new ArrayList<String>() : new HashSet<String>();

null 値などのあいまいな場合は、クラスObjectが推測されます。

5. 構成キー

Lombokでは、プロジェクト全体で1つのファイルに機能を構成することができます。 したがって、プロジェクトのディレクティブと設定を1か所に含めることができます。

プロジェクトで開発標準を実施する一環として、Lombokのvarおよびvalの使用を制限したい場合があります。 また、誰かが誤ってそれらを使用した場合、コンパイル中に警告を生成したい場合があります。

そのような場合、 lombok.configファイルに以下を含めることで、varまたはvalの使用を警告またはエラーとしてフラグを立てることができます。

lombok.var.flagUsage = error
lombok.val.flagUsage = warning

プロジェクト全体でのvarの不正使用に関するエラーが表示されます。

[12,13] Use of var is flagged according to lombok configuration.

同様に、valの使用に関する警告メッセージが表示されます。

ValExample.java:18: warning: Use of val is flagged according to lombok configuration.
val age = Integer.valueOf(30);

6. 結論

この記事では、Lombokを使用して、型を指定せずにローカル変数を定義する方法を示しました。 さらに、valおよびvar変数の宣言の複雑さを学びました。

また、ローカル変数の一般的な宣言が複合型でどのように機能するかを示しました。

いつものように、コードはGitHubから入手できます。