イミュータブルとは即ちオブジェクトがコンピュータのメモリ中で書き込み不可能であるという意味ではない。むしろイミュータブルはコンパイル時の問題であり、プログラマが「何をすべきか」であって、必ずしも「何ができるか」ではない。例えばCやC++で型システムの裏をかくのを禁止するためのものではない。ミュータブルとイミュータブルの利点をいいとこ取りする、モダンなハードウェアがサポートしているテクニックはコピーオンライトである。このテクニックでは、利用者がシステムにオブジェクトを複製するように命じた際に、代わりに同一のオブジェクトを指す参照を作る。利用者がある参照を通してそのオブジェクトに変更を加えると、直ちに本物の複製を作ってそれを指すように参照を書き換える。これによって他の利用者は影響されない。なぜなら、依然オリジナルのオブジェクトを参照しているからである。したがって、コピーオンライト環境ではすべての利用者はオブジェクトをミュータブルなものとして持っているように見えるが、利用者がそのオブジェクトを書き換えない限り、イミュータブルなオブジェクトの実行効率も獲得できる。コピーオンライトは仮想記憶システムでよく利用され、プログラムが他のプログラムにメモリを書き換えられる心配なしにメモリを節約することができる。イミュータブルなオブジェクトの古典的な例はJavaのStringクラスのインスタンスである。String str = "ABC";str.toLowerCase();メソッドtoLowerCase()は変数strの値"ABC"を書き換えない。代わりに新しいStringオブジェクトがインスタンス化され、生成時に"abc"という値が与えられる。このStringオブジェクトへの参照はtoLowerCase()が返す。変数strに値"abc"を持たせたいのなら、str = str.toLowerCase();とする必要がある。Stringクラスのメソッドはインスタンスの持つデータを書き換えることがない。オブジェクトがイミュータブルであるためには、フィールドがミュータブルであるかどうかとは別に、そのフィールドを書き換える方法があってはならず、またミュータブルなフィールドを読み書きする方法があってはならない。Javaでミュータブルなオブジェクトの例を示す。class Cart<T> {  private final List<T> items;  public Cart(List<T> items) { this.items = items; }  public List<T> getItems() { return items; }  public int total() { /* return sum of the prices */ }}このクラスのインスタンスはイミュータブルではない。なぜなら、getItems()を呼んでitemsフィールドを得たり、インスタンス化の際に渡したListオブジェクトを保持し続けることで、フィールドが書き換え可能だからである。以下のImmutableCartクラスは部分的にイミュータブルになるように書き換えた例である。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);  }  public int total() { /* return sum of the prices */ }}次にRubyでのほぼ同じ例を載せる。class Cart  def initialize(items)    @items = items.dup.freeze  end  def items    @items.clone  end  def total    # sum of the prices  endendもはやitemsを書き換えることはできない。しかし、リストitemsの要素もイミュータブルであるという保証はない。解決法の一つとしてはDecorator パターンでリストの各要素をラップしてしまうというものがある。C++ではCartをconst-correctな実装にすることで、インスタンスをイミュータブル(const)としてもミュータブルとしても好きなように宣言できる。つまり、2つの異なるgetItems()を提供するのである。template<typename T>class Cart { private:  std::vector<T> items; public:  Cart(std::vector<T> v): items(v) { }  std::vector<T>& getItems() { return items; }  const std::vector<T>& getItems() const { return items; }  int total() const { /* return sum of the prices */ }};前述のJavaの例ではコンストラクタ引数を参照として保持していたが、C++ではメンバ変数に格納する時点で複製を行なっている。これはC++に自動ガベージコレクションがないための定石によるものである。こうしてメンバ変数がすでに複製であることから、getItems() constの内容はJavaと違って戻り値の型にconstを加えるだけでよい。このC++の例はイミュータブル・ミュータブル兼用として作成されたものだが、コンストラクタについて二つのバージョンを用意する必要はないし、実際にできない。Cart型の変数を宣言するときにconstを付けるかどうかを決めるだけである。前述のJavaのコードがイミュータブルではない理由は他にもある。クラスが継承可能であるということである。サブクラスで勝手にitemsを書き換えるsetterメソッドを実装される恐れがある。そこでclassにfinal修飾子を付加する。念のためメソッド引数にもfinalをつけておく。さらに、「防御的コピー」という手法を用いて、getItems()で取り出したListを変更されても、ImmutableCartクラスが持っているitemsフィールドの内容まで変更されないよう、フィールドをコピーしておく。上のほうでは、Listを一旦配列に変換してから戻すという手法が使われたが、ここでは替わりにObject.clone(Object)メソッドを用いて解決する。引数のオブジェクトを一旦clone()でコピーしてからフィールドに代入することで、引数に渡す前のListオブジェクトとの参照を切り離すことができ、引数に渡す前のListオブジェクトを変更してもImmutableCartにあるListオブジェクトitemsにまで変更が及ばないようにすることができる。これは、getItems()メソッドでも同様に行う必要がある。Collections.checkedList(List<T>,Class<T>)は、itemsが他のメソッドに渡されたときにリストにTとは異なる型が代入されるのを防ぐためにある。パラメタライズされていない非汎用のListでしか動作しないメソッドに渡すと、そのメソッドはそのListに対して型Tではない要素を不正に追加する恐れがある。Collections.unmodifiableList(List<T>)はリストの要素を変更できないよう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(); //内部フィールドの防御的コピー  }  public int total() { /* return sum of the prices */ }}また、クラスが不変であるかどうかを確認する方法の一つとしてFindBugsというツールを使うという手がある。これはバグの温床になるコードを自動検出してくれて、不変クラスを作るときにも貢献する。