Javaの不変オブジェクト

Javaの不変オブジェクト

1. 概要

このチュートリアルでは、オブジェクトを不変にする理由、Javaで不変性を実現する方法、およびそうすることで得られる利点について学習します。

2. 不変オブジェクトとは何ですか?

不変オブジェクトはobject whose internal state remains constant after it has been entirely createdです。

これは、不変オブジェクトのパブリックAPIが、そのライフタイム全体を通じて同じように動作することを保証することを意味します。

クラスStringを見ると、そのAPIがreplaceメソッドで変更可能な動作を提供しているように見えても、元のStringは変更されていないことがわかります。

String name = "example";
String newName = name.replace("dung", "----");

assertEquals("example", name);
assertEquals("bael----", newName);

APIは読み取り専用のメソッドを提供します。オブジェクトの内部状態を変更するメソッドを含めることはできません。

3. Javaのfinalキーワード

Javaで不変性を実現する前に、finalキーワードについて説明する必要があります。

Javaでは、variables are mutable by default, meaning we can change the value they hold

変数を宣言するときにfinalキーワードを使用することにより、Javaコンパイラはその変数の値を変更できなくなります。 代わりに、コンパイル時エラーを報告します。

final String name = "example";
name = "bael...";

finalは、変数が保持する参照を変更することを禁止するだけであり、パブリックAPIを使用して参照するオブジェクトの内部状態を変更することを保護するものではないことに注意してください。

final List strings = new ArrayList<>();
assertEquals(0, strings.size());
strings.add("example");
assertEquals(0, strings.size());

リストに要素を追加するとサイズが変わるため、2番目のassertEqualsは失敗します。したがって、その要素は不変オブジェクトではありません。

4. Javaの不変性

変数のコンテンツへの変更を回避する方法がわかったので、それを使用して不変オブジェクトのAPIを構築できます。

不変オブジェクトのAPIを構築するには、APIの使用方法に関係なく内部状態が変化しないことを保証する必要があります。

正しい方向への一歩は、属性を宣言するときにfinalを使用することです。

class Money {
    private final double amount;
    private final Currency currency;

    // ...
}

Javaは、amountの値が変更されないことを保証していることに注意してください。これは、すべてのプリミティブ型変数の場合です。

ただし、この例では、currencyが変更されないことが保証されているだけなので、we must rely on the CurrencyAPI to protect itself from changesです。

ほとんどの場合、カスタム値を保持するにはオブジェクトの属性が必要であり、不変オブジェクトの内部状態を初期化する場所はそのコンストラクターです。

class Money {
    // ...
    public Money(double amount, Currency currency) {
        this.amount = amount;
        this.currency = currency;
    }

    public Currency getCurrency() {
        return currency;
    }

    public double getAmount() {
        return amount;
    }
}

前に述べたように、不変のAPIの要件を満たすために、Moneyクラスには読み取り専用メソッドのみがあります。

リフレクションAPIを使用すると、不変性とchange immutable objectsを破ることができます。 ただし、リフレクションは不変オブジェクトのパブリックAPIに違反しているため、通常はこれを回避する必要があります。

5. 利点

不変オブジェクトの内部状態は時間的に一定のままであるため、we can share it safely among multiple threads

自由に使用することもでき、それを参照しているオブジェクトはどれも違いに気付かないので、immutable objects are side-effects freeと言えます。

6. 結論

不変オブジェクトは時間内に内部状態を変更せず、スレッドセーフで副作用がありません。 これらのプロパティがあるため、不変オブジェクトはマルチスレッド環境を扱うときに特に役立ちます。

この記事over on GitHubで使用されている例を見つけることができます。