不変物の紹介

不変の概要

1. 前書き

この記事では、Immutablesライブラリを操作する方法を示します。

Immutablesは、シリアル化およびカスタマイズ可能な不変オブジェクトを生成および操作するための注釈と注釈プロセッサで構成されます。

2. Mavenの依存関係

プロジェクトでImmutablesを使用するには、pom.xmlファイルのdependenciesセクションに次の依存関係を追加する必要があります。


    org.immutables
    value
    2.2.10
    provided

このアーティファクトは実行時に必要ないため、providedスコープを指定することをお勧めします。

ライブラリの最新バージョンはhereにあります。

3. 不変

ライブラリは、抽象型(InterfaceClassAnnotation)から不変オブジェクトを生成します。

これを実現するための鍵は、@Value.Immutableアノテーションを適切に使用することです。 It generates an immutable version of an annotated type and prefixes its name with the Immutable keyword

X」という名前のクラスの不変バージョンを生成しようとすると、“ImmutableX”.という名前のクラスが生成されます。生成されたクラスは再帰的に不変ではないため、この点に注意してください。

また、簡単なメモ– Immutablesは注釈処理を使用するため、IDEで注釈処理を有効にすることを忘れないでください。

3.1. Abstract ClassesおよびInterfaces@Value.Immutableを使用する

生成されるフィールドを表す2つのabstractアクセサメソッドで構成される単純なabstractクラスPersonを作成し、@Value.Immutableアノテーションでクラスにアノテーションを付けましょう。

@Value.Immutable
public abstract class Person {

    abstract String getName();
    abstract Integer getAge();

}

注釈処理が完了すると、すぐに使用できるnewly-generated ImmutablePerson class in a target/generated-sources directoryが見つかります。

@Generated({"Immutables.generator", "Person"})
public final class ImmutablePerson extends Person {

    private final String name;
    private final Integer age;

    private ImmutablePerson(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    @Override
    String getName() {
        return name;
    }

    @Override
    Integer getAge() {
        return age;
    }

    // toString, hashcode, equals, copyOf and Builder omitted

}

生成されたクラスには、実装されたtoStringhashcodeequalsメソッド、およびステップビルダーImmutablePerson.Builderが付属しています。 生成されたコンストラクターにはprivateアクセスがあることに注意してください。

ImmutablePersonクラスのインスタンスを構築するには、PersonオブジェクトからImmutablePersonコピーを作成できるビルダーまたは静的メソッドImmutablePerson.copyOf,を使用する必要があります。

ビルダーを使用してインスタンスを構築する場合、次のように簡単にコーディングできます。

ImmutablePerson john = ImmutablePerson.builder()
  .age(42)
  .name("John")
  .build();

生成されたクラスは不変であるため、変更できません。 既存のオブジェクトを変更する場合は、「withX」メソッドの1つを使用できます。このメソッドは、元のオブジェクトを変更せず、変更されたフィールドを使用して新しいインスタンスを作成します。

john’sの経過時間を更新して、新しいjohn43オブジェクトを作成しましょう。

ImmutablePerson john43 = john.withAge(43);

このような場合、次のアサーションが真になります。

assertThat(john).isNotSameAs(john43);
assertThat(john.getAge()).isEqualTo(42);

4. 追加のユーティリティ

そのようなクラスの生成は、それをカスタマイズできなければ、あまり役に立ちません。 Immutablesライブラリには、@Value.Immutableの出力をカスタマイズするために使用できる一連の追加の注釈が付属しています。 それらすべてを確認するには、Immutablesのdocumentationを参照してください。

4.1. @Value.Parameter注釈

@Value.Parameterアノテーションは、コンストラクターメソッドを生成する必要があるフィールドを指定するために使用できます。

このようにクラスに注釈を付ける場合:

@Value.Immutable
public abstract class Person {

    @Value.Parameter
    abstract String getName();

    @Value.Parameter
    abstract Integer getAge();
}

次の方法でインスタンス化できます。

ImmutablePerson.of("John", 42);

4.2. @Value.Default注釈

@Value.Defaultアノテーションを使用すると、初期値が指定されていない場合に使用するデフォルト値を指定できます。 これを行うには、固定値を返す非抽象アクセサメソッドを作成し、それに@Value.Defaultアノテーションを付ける必要があります。

@Value.Immutable
public abstract class Person {

    abstract String getName();

    @Value.Default
    Integer getAge() {
        return 42;
    }
}

次のアサーションが真になります。

ImmutablePerson john = ImmutablePerson.builder()
  .name("John")
  .build();

assertThat(john.getAge()).isEqualTo(42);

4.3. @Value.Auxiliary注釈

@Value.Auxiliaryアノテーションは、オブジェクトのインスタンスに格納されるプロパティにアノテーションを付けるために使用できますが、equalshashCode、およびtoStringの実装では無視されます。

このようにクラスに注釈を付ける場合:

@Value.Immutable
public abstract class Person {

    abstract String getName();
    abstract Integer getAge();

    @Value.Auxiliary
    abstract String getAuxiliaryField();

}

auxiliaryフィールドを使用する場合、次のアサーションが当てはまります。

ImmutablePerson john1 = ImmutablePerson.builder()
  .name("John")
  .age(42)
  .auxiliaryField("Value1")
  .build();

ImmutablePerson john2 = ImmutablePerson.builder()
  .name("John")
  .age(42)
  .auxiliaryField("Value2")
  .build();
assertThat(john1.equals(john2)).isTrue();
assertThat(john1.toString()).isEqualTo(john2.toString());
assertThat(john1.hashCode()).isEqualTo(john2.hashCode());

4.4. @Value.Immutable(Prehash = True)注釈

生成されたクラスは不変であり、変更することはできないため、hashCodeの結果は常に同じままであり、オブジェクトのインスタンス化中に1回だけ計算できます。

このようにクラスに注釈を付ける場合:

@Value.Immutable(prehash = true)
public abstract class Person {

    abstract String getName();
    abstract Integer getAge();

}

生成されたクラスを調べると、hashcodeの値が事前に計算され、フィールドに格納されていることがわかります。

@Generated({"Immutables.generator", "Person"})
public final class ImmutablePerson extends Person {

    private final String name;
    private final Integer age;
    private final int hashCode;

    private ImmutablePerson(String name, Integer age) {
        this.name = name;
        this.age = age;
        this.hashCode = computeHashCode();
    }

    // generated methods

    @Override
    public int hashCode() {
        return hashCode;
    }
}

hashCode()メソッドは、オブジェクトの作成時に生成された、事前に計算されたhashcodeを返します。

5. 結論

このクイックチュートリアルでは、Immutablesライブラリの基本的な動作を示しました。

この記事のすべてのソースコードと単体テストは、GitHub repositoryにあります。