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
の
final
キーワード
Javaで不変性を実現しようとする前に、
final
キーワードについて話す必要があります。
Javaでは、
変数はデフォルトで変更可能です。つまり、
を保持する値を変更できます。
変数を宣言するときに
final
キーワードを使用することによって、Javaコンパイラはその変数の値を変更させません。代わりに、コンパイル時エラーを報告します。
final String name = "baeldung";
name = "bael...";
final
は、変数が保持する参照を変更することを禁止しているだけであり、それがそのパブリックAPIを使用して参照しているオブジェクトの内部状態を変更することを防ぐことはできません。
final List<String> 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
の値が変わらないことを保証していることに注意してください。これはすべてのプリミティブ型変数に当てはまります。
ただし、この例では
currency
が変更されないことが保証されているだけなので、
変更から自分自身を保護するには
Currency
APIに依存する必要があります
。
ほとんどの場合、カスタム値を保持するためにオブジェクトの属性が必要です。不変オブジェクトの内部状態を初期化する場所はそのコンストラクタです。
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. 結論
不変オブジェクトは内部状態を時間内に変更することはありません。スレッドセーフであり、副作用はありません。これらのプロパティのため、不変オブジェクトはマルチスレッド環境を扱うときにも特に便利です。
この記事https://github.com/eugenp/tutorials/tree/master/core-java-lang-oop[over on GitHub]で使用されている例を見つけることができます。