イミュータブル

イミュータブル () なオブジェクトとは、作成後にその状態を変えることのできないオブジェクトのことである。対義語はミュータブル () なオブジェクトで、作成後も状態を変えることができる。は「変更可能な」、は「変更不可能な、不変の」という意味を持つ形容詞である。
あるオブジェクト全体がイミュータブルなこともあるし、C++でcodice_1データメンバを使う場合など、一部の属性のみがイミュータブルなこともある。場合によっては、内部で使われている属性が変化しても、外部からオブジェクトの状態が変化していないように見えるならば、オブジェクトをイミュータブルとみなすことがある。例えば、コストの高い計算の結果をキャッシュするためにメモ化を利用していても、そのオブジェクトは依然イミュータブルとみなせる。イミュータブルなオブジェクトの初期状態は大抵は生成時に設定されるが、オブジェクトが実際に使用されるまで遅らせることもある。
イミュータブルなオブジェクトを使うと、複製や比較のための操作を省けるため、コードが単純になり、また性能の改善にもつながる。しかしオブジェクトが変更可能なデータを多く持つ場合には、イミュータブル化は不適切となることが多い。このため、多くのプログラミング言語ではイミュータブルかミュータブルか選択できるようにしている。
背景.
ほとんどのオブジェクト指向言語では、オブジェクトは参照の形でやり取りされる。Java・C++・Perl・Python・Rubyなどがその例である。この場合、オブジェクトが参照を通じて共有されていると、その状態が変更される可能性が問題となる。
もしオブジェクトがイミュータブルであったなら、オブジェクトの複製はオブジェクト全体の複製ではなく、単に参照の複製で済む。参照は通常オブジェクト自体よりもずっと小さいので（典型的にはポインタのサイズのみ）、メモリが節約でき、プログラムの実行速度もよくなる。
参照コピーのテクニックは、ミュータブルなオブジェクトに対して使用するのはさらに難しくなる。なぜならば、ミュータブルなオブジェクトの参照を保持する者が1人でもオブジェクトに変更を加えると、参照を共有する者全員がその影響を受けるからである。これが意図した作用でないならば、その他の参照の保持者に正しく対処してもらうよう通知するのは難しくなる可能性がある。このような場合、参照ではなくオブジェクト全体の () を作成するのが一般的な解決法だが、これは簡単ではあるもののコストがかかる。他にはObserver パターンがミュータブルなオブジェクトへの変更に対処するのに利用できる。
イミュータブルなオブジェクトはマルチスレッドプログラミングにおいても有用となる。データがイミュータブルなオブジェクトで表現されていると、複数のスレッドが他のスレッドにデータを変更される心配なくデータにアクセスできる。つまり排他制御の必要がない。よってイミュータブルなオブジェクトのほうがミュータブルなものよりスレッドセーフであると考えられる。
等価なオブジェクトのコピーを作成する代わりに常に参照を複製するというテクニックはとして知られる。インターンが使われているならば、2つのオブジェクトが等しいとみなされるのは、2つの参照が等しい場合でありかつその場合に限られる。いくつかの言語では自動的にインターンが行なわれる。例えばPythonでは文字列を自動的にインターンする。インターンを実装したアルゴリズムで、可能な場合は常にインターンすることが保証されているならば、オブジェクトの等価性の比較はそのポインタの比較に帰着し、多くのアプリケーションで高速化が達成できる。またアルゴリズムがそのような保証をしない場合でも、オブジェクトの比較の平均的なコストを下げることができる。一般的に、インターンの価値があるのはオブジェクトがイミュータブルなときだけである。
実装.
イミュータブルとは即ちオブジェクトがコンピュータのメモリ中で書き込み不可能であるという意味ではない。むしろイミュータブルはコンパイル時の問題であり、プログラマが「何をすべきか」であって、必ずしも「何ができるか」ではない。例えばCやC++で型システムの裏をかくのを禁止するためのものではない。
ミュータブルとイミュータブルの利点をいいとこ取りする、モダンなハードウェアがサポートしているテクニックはコピーオンライトである。このテクニックでは、利用者がシステムにオブジェクトを複製するように命じた際に、代わりに同一のオブジェクトを指す参照を作る。利用者がある参照を通してそのオブジェクトに変更を加えると、直ちに本物の複製を作ってそれを指すように参照を書き換える。これによって他の利用者は影響されない。なぜなら、依然オリジナルのオブジェクトを参照しているからである。したがって、コピーオンライト環境ではすべての利用者はオブジェクトをミュータブルなものとして持っているように見えるが、利用者がそのオブジェクトを書き換えない限り、イミュータブルなオブジェクトの実行効率も獲得できる。コピーオンライトは仮想記憶システムでよく利用され、プログラムが他のプログラムにメモリを書き換えられる心配なしにメモリを節約することができる。
イミュータブルなオブジェクトの古典的な例はJavaのクラスのインスタンスである。
String str = "ABC";
str.toLowerCase();
メソッドcodice_2は変数codice_3の値"ABC"を書き換えない。代わりに新しいcodice_4オブジェクトがインスタンス化され、生成時に"abc"という値が与えられる。このcodice_4オブジェクトへの参照はcodice_2が返す。変数codice_3に値"abc"を持たせたいのなら、
str = str.toLowerCase();
とする必要がある。codice_4クラスのメソッドはインスタンスの持つデータを書き換えることがない。
オブジェクトがイミュータブルであるためには、フィールドがミュータブルであるかどうかとは別に、そのフィールドを書き換える方法があってはならず、またミュータブルなフィールドを読み書きする方法があってはならない。Javaでミュータブルなオブジェクトの例を示す。
class Cart<T> {
 private final List<T> items;
このクラスのインスタンスはイミュータブルではない。なぜなら、codice_9を呼んでcodice_10フィールドの参照コピーを得たり、インスタンス化の際に渡したcodice_11オブジェクトの参照を保持し続けたりすることで、codice_10フィールドが指しているcodice_11オブジェクトの内容を書き換えることが可能だからである。以下のcodice_14クラスは部分的にイミュータブルになるように書き換えた例である。
class ImmutableCart<T> {
 private final List<T> items;
 public ImmutableCart(List<T> items) {
 this.items = Arrays.asList(items.toArray());
 public List<T> getItems() {
 return Collections.unmodifiableList(items);
次にRubyでのほぼ同じ例を載せる。
class Cart
 def initialize(items)
 @items = items.dup.freeze
 end
 def items
 @items.clone
 end
 def total
 # sum of the prices
 end
end
もはやcodice_10を書き換えることはできない。しかし、リストcodice_10の要素もイミュータブルであるという保証はない。解決法の一つとしてはDecorator パターンでリストの各要素をラップしてしまうというものがある。
C++ではcodice_17をconst-correctな実装にすることで、インスタンスをイミュータブル (codice_1) としてもミュータブルとしても好きなように宣言できる。つまり、2つの異なるcodice_9を提供するのである。
template<typename T>
class Cart {
private:
 std::vector<T> m_items;
public:
前述のJavaの例ではコンストラクタ引数の参照コピーとしてフィールドに保持していたが、C++ではメンバー変数に格納する時点で複製を行なっている。これはC++にガベージコレクションがないための定石によるものである。メンバー変数codice_20をポインタ型または参照型にすることもできるが、そうするとコンストラクタ呼び出し元で渡した変数の寿命が尽きた場合に無効なアドレスを指すダングリングポインタまたはダングリング参照となってしまい、管理が煩雑となる。
上記の例では、ミュータブル用のcodice_9はメンバー変数codice_20への参照を直接返しており、この参照経由でオブジェクトの状態を変更することができる。メンバー変数codice_20がすでに複製であることから、イミュータブル用のcodice_24の内容は戻り値の型にcodice_1を加えるだけでよい。codice_1修飾されたオブジェクトからはcodice_1修飾されたメンバーにしかアクセスできないため、破壊的な操作はできなくなる。ただし、内部状態への参照を返すと、codice_17型変数が破棄されたときにその参照も無効となってしまうため、扱いに注意が必要である。安全のため、イミュータブル用のcodice_24の戻り値の型はcodice_30として、ディープコピーを返す実装とすることもある（防衛的コピー）。
このC++の例はイミュータブル／ミュータブル兼用として作成されたものだが、コンストラクタについて2つのバージョンを用意する必要はないし、実際にできない。codice_17型の変数を宣言するときにcodice_1を付けるかどうかを決めるだけである。
なお、C++でも共有ポインタ（参照カウント方式のスマートポインタ）を使えば、Javaのようにコンストラクタ呼び出し元と状態を共有する設計にすることもできる。共有ポインタのコピーは、配列全体のコピーよりもコストがずっと小さいため、場合によってはあえてこちらのミュータブルな設計を選択することもある。
template<typename T>
class Cart {
private:
 std::shared_ptr<std::vector<T» m_items;
public:
前述のJavaのコードがイミュータブルではない理由は他にもある。クラスが継承可能であるということである。サブクラスで勝手にcodice_10を書き換えるsetterメソッドを実装される恐れがある。そこでclassにcodice_34修飾子を付加する。念のためメソッド引数にもcodice_34をつけておく。さらに、「防衛的コピー」という手法を用いて、codice_9で取り出したcodice_11を変更されても、codice_14クラスが持っているcodice_10フィールドの内容まで変更されないよう、フィールドをコピーしておく。上の例では、codice_11を一旦配列に変換してから戻すという手法が使われたが、ここでは替わりにメソッドを用いて解決する。引数のオブジェクトを一旦codice_41でコピーしてからフィールドに代入することで、引数に渡す前のcodice_11オブジェクトとの参照を切り離すことができ、引数に渡す前のcodice_11オブジェクトを変更してもcodice_14にあるcodice_11オブジェクトのcodice_10にまで変更が及ばないようにすることができる。これは、codice_9メソッドでも同様に行う必要がある。
は、codice_10が他のメソッドに渡されたときにリストにcodice_49とは異なる型が代入されるのを防ぐためにある。パラメータ化されていない非ジェネリックのcodice_11でしか動作しないメソッドに渡すと、そのメソッドはそのcodice_11に対して型codice_49ではない要素を不正に追加する恐れがある。はリストの要素を変更できないようfinalにするためにある。
final class ImmutableCart<T> {
 private final List<T> items;
 public ImmutableCart(final List<T> items) {
 // 防衛的コピーを行った後、リストの要素の実行時型チェックを行ない、
 // リストの要素をfinalにする。
 this.items = Collections.unmodifiableList(
 Collections.checkedList(
 (List<T>) items.clone(), T.class
 );
 public List<T> getItems() {
 return (List<T>) items.clone(); // 内部フィールドの防衛的コピー。
また、クラスが不変であるかどうかを確認する方法の一つとしてFindBugsというツールを使うという手がある。これはバグの温床になるコードを自動検出してくれて、不変クラスを作るときにも貢献する。
外部リンク.
英語