#Immutableパターン
Stringクラスには、文字列の内容を変更するメソッドが用意されていない。Stringのインスタンスが表している文字列の内容は決して変化しない。このことからStringクラスのメソッドは、synchronizedにする必要がない。Immutableは、「不変の」、「変わることがない」という意味。Immutableパターンには、インスタンスの状態が変わらないことが保証されているクラス(immutableなクラス)が登場する。排他制御が不要のため、うまく利用すればパフォーマンス向上を期待できる。
(コード全体は本書を参照のこと)
public final class Person {
private final String name;
private final String hobby;
public Person(String name, String hobby) {
this.name = name;
this.hobby = hobby;
}
public String getName() {
return name;
}
public String getHobby() {
return hobby;
}
}
このクラスはPersonクラスのフィールドの値は、コンストラクタでのみ設定可能。またフィールドの値を変更するメソッドもない。したがって、Personクラスのインスタンスは、いったん作られてしまうと、フィールドの値がかわることはない。このとき、複数のスレッドから同時にアクセスされても、Personクラスは安全である。いずれのメソッドもsynchronizedにする必要がない。
##登場人部
Immutable役が登場する。Immutable役は、フィールドの値を変更することができず、フィールドの内容を変更するメソッドも持っていないクラス。
##どんなときに使うのか
- インスタンスの生成後、状態が変化しないとき。それは、次のときである。フィールドがfinalになっていること、setterメソッドが存在しないこと、フィールドが参照している先のインスタンスが変化しないこと。
以下のクラスはimmutableではないので注意。
public final class Hoge {
private final StringBuffer hoge;
public Hoge(String a, String b) {
this.hoge = new StringBuffer("a=" + a + ", b=" + b);
}
public StringBuffer getHoge() {
return hoge;
}
}
getHogeメソッドで得られるhogeフィールドが持っているインスタンスは、Stringではなく、StringBufferのインスタンス。StringBufferクラスは内部状態を変更するメソッドを持っているため、hogeフィールドの内容を外部から書き換えることができてしまう。hogeフィールドはfinal宣言されているため、フィールドの値そのものは変化しないが、そのフィールド(ポインタと思えば良い)が指している先にあるインスタンスの状態は変化する可能性がある。
- インスタンスが共有され、頻繁にアクセスされるとき。
##mutableなクラスとimmuatbleなクラス
Javaの標準クラスライブラリには、mutableなクラスとimmutableなクラスが対になっているものがある。例えば、StringBufferクラスとStringクラス。StringBufferは書き換えの際には適切にsynchronizedが使われている。一方StringクラスはSynchronizedを使用していないため、高速な参照が可能。内容を頻繁に変更するならStringBufferを使い、内容を変更する必要がなく参照するだけならStringを使う。
##不変性を守るために
Immuableパターンであることを前提にsynchronizedを取り除いている場合、不変性が失われたとたん、クラスの安全性まで失われてしまうことになってしまう。そのため、プログラムのコメントやAPIドキュメントで、不変性について明記しておくべき。
関連
『Java言語で学ぶデザインパターン(マルチスレッド編)』まとめ(その1)
『Java言語で学ぶデザインパターン(マルチスレッド編)』まとめ(その2)
『Java言語で学ぶデザインパターン(マルチスレッド編)』まとめ(その3)