Javaの不変オブジェクト

1。概要

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

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

不変オブジェクトとは、完全に作成された後も内部状態が一定のままであるオブジェクトです

これは、不変オブジェクトのパブリックAPIが、その存続期間全体にわたって同じように動作することを保証することを意味します。

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

String name = "baeldung"; String newName = name.replace("dung", "----"); assertEquals("baeldung", name); assertEquals("bael----", newName);

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

3.Javaの最後のキーワード

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

Javaでは、変数はデフォルトで変更可能です。つまり変数が保持する値を変更できます

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

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

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

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

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

4.Javaの不変性

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

不変オブジェクトのAPIを構築するには、APIをどのように使用しても内部状態が変化しないことを保証する必要があります。

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

class Money { private final double amount; private final Currency currency; // ... }

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

ただし、この例では、通貨が変更されないことが保証されているだけなのでCurrencyAPIを使用して変更から自身を保護する必要があります

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

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を使用すると、不変性を破り、不変オブジェクトを変更できます。ただし、リフレクションは不変オブジェクトのパブリックAPIに違反するため、通常はこれを回避する必要があります。

5.メリット

不変オブジェクトの内部状態は時間的に一定であるため、複数のスレッド間で安全に共有できます

また、自由に使用することもでき、それを参照するオブジェクトはどれも違いに気付かず、不変オブジェクトは副作用がないと言えます

6.結論

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

この記事で使用されている例は、GitHubにあります。