イミュータブル
ほとんどのオブジェクト指向言語では、オブジェクトは参照の形でやり取りされる。Java・C++・Perl・Python・Rubyなどがその例である。この場合、オブジェクトが参照を通じて共有されていると、その状態が変更される可能性が問題となる。
もしオブジェクトがイミュータブルであったなら、オブジェクトの複製はオブジェクト全体の複製ではなく、単に参照の複製で済む。参照は通常オブジェクト自体よりもずっと小さいので(典型的にはポインタのサイズのみ)、メモリが節約でき、プログラムの実行速度もよくなる。
参照コピーのテクニックは、ミュータブルなオブジェクトに対して使用するのはさらに難しくなる。なぜならば、ミュータブルなオブジェクトの参照を保持する者が1人でもオブジェクトに変更を加えると、参照を共有する者全員がその影響を受けるからである。これが意図した作用でないならば、その他の参照の保持者に正しく対処してもらうよう通知するのは難しくなる可能性がある。このような場合、参照ではなくオブジェクト全体の防衛的(防御的)コピー(英語版) (defensive copy) を作成するのが一般的な解決法だが、これは簡単ではあるもののコストがかかる。他にはObserver パターンがミュータブルなオブジェクトへの変更に対処するのに利用できる。
メソッドtoLowerCase()は変数strの値"ABC"を書き換えない。代わりに新しいStringオブジェクトがインスタンス化され、生成時に"abc"という値が与えられる。このStringオブジェクトへの参照はtoLowerCase()が返す。変数strに値"abc"を持たせたいのなら、
とする必要がある。Stringクラスのメソッドはインスタンスの持つデータを書き換えることがない。
オブジェクトがイミュータブルであるためには、フィールドがミュータブルであるかどうかとは別に、そのフィールドを書き換える方法があってはならず、またミュータブルなフィールドを読み書きする方法があってはならない。Javaでミュータブルなオブジェクトの例を示す。
このクラスのインスタンスはイミュータブルではない。なぜなら、getItems()を呼んでitemsフィールドの参照コピーを得たり、インスタンス化の際に渡したListオブジェクトの参照を保持し続けたりすることで、itemsフィールドが指しているListオブジェクトの内容を書き換えることが可能だからである。以下のImmutableCartクラスは部分的にイミュータブルになるように書き換えた例である。
次にRubyでのほぼ同じ例を載せる。
もはやitemsを書き換えることはできない。しかし、リストitemsの要素もイミュータブルであるという保証はない。
ただし、内部状態への参照を返すと、Cart型変数が破棄されたときにその参照も無効となってしまうため、扱いに注意が必要である。安全のため、イミュータブル用のgetItems() constの戻り値の型はstd::vector<T>として、ディープコピーを返す実装とすることもある(防衛的コピー)。
このC++の例はイミュータブル/ミュータブル兼用として作成されたものだが、コンストラクタについて2つのバージョンを用意する必要はないし、実際にできない。Cart型の変数を宣言するときにconstを付けるかどうかを決めるだけである。
なお、C++でも共有ポインタ(参照カウント方式のスマートポインタ)を使えば、Javaのようにコンストラクタ呼び出し元と状態を共有する設計にすることもできる。共有ポインタのコピーは、配列全体のコピーよりもコストがずっと小さいため、場合によってはあえてこちらのミュータブルな設計を選択することもある。
前述のJavaのコードがイミュータブルではない理由は他にもある。クラスが継承可能であるということである。サブクラスで勝手にitemsを書き換えるsetterメソッドを実装される恐れがある。そこでclassにfinal修飾子を付加する。念のためメソッド引数にもfinalをつけておく。さらに、「防衛的コピー」という手法を用いて、getItems()で取り出したListを変更されても、ImmutableCartクラスが持っているitemsフィールドの内容まで変更されないよう、フィールドをコピーしておく。