イミュータブル
実装

イミュータブルとは即ちオブジェクトがコンピュータのメモリ中で書き込み不可能であるという意味ではない。むしろイミュータブルはコンパイル時の問題であり、プログラマが「何をすべきか」であって、必ずしも「何ができるか」ではない。例えば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オブジェクトの参照を保持し続けたりすることで、itemsフィールドが指しているListオブジェクトの内容を書き換えることが可能だからである。以下のImmutableCartクラスは部分的にイミュータブルになるように書き換えた例である。
class ImmutableCart<T> {
  private final List<T> items;

  public ImmutableCart(List<T> items) {
    //this.items = Arrays.asList(items.toArray()); // ジェネリクスでは使えない。
    //this.items = List.copyOf(items); // Java 10 以降で使えるものの、要素として null を含んでいた場合は NullPointerException がスローされてしまう。
    this.items = new ArrayList<T>();
    this.items.addAll(items);
  }

  public List<T> getItems() {
    return Collections.unmodifiableList(items);
  }
  public int total() { /* return sum of the prices */ }
}
java.util.Collections.unmodifiableList(List<T>)は、引数で指定されたリストの変更不可能なビューを返す。つまり、読み取り専用のアクセスのみを許可する（返却されたリストを書き換えようとした場合はUnsupportedOperationExceptionがスローされる）。
したがって、もはやitemsを書き換えることはできない。しかし、リストitemsの要素もイミュータブルであるという保証はない。解決法の一つとしてはDecorator パターンでリストの各要素をラップしてしまうというものがある。
C++ではCartをconst-correctな実装にすることで、インスタンスをイミュータブル (const) としてもミュータブルとしても好きなように宣言できる。つまり、2つの異なるgetItems()を提供するのである。
template<typename T>
class Cart {
private:
  std::vector<T> m_items;

public:
  explicit Cart(const std::vector<T>& items) : m_items(items) { }

  std::vector<T>& getItems() { return m_items; }
  const std::vector<T>& getItems() const { return m_items; }
  int total() const { /* return sum of the prices */ }
};
前述のJavaの例ではコンストラクタ引数の参照コピーとしてフィールドに保持していたが、C++ではメンバー変数に格納する時点で複製を行なっている。これはC++にガベージコレクションがないための定石によるものである。メンバー変数m_itemsをポインタ型または参照型にすることもできるが、そうするとコンストラクタ呼び出し元で渡した変数の寿命が尽きた場合に無効なアドレスを指すダングリングポインタまたはダングリング参照となってしまい、管理が煩雑となる。
上記の例では、ミュータブル用のgetItems()はメンバー変数m_itemsへの参照を直接返しており、この参照経由でオブジェクトの状態を変更することができる。メンバー変数m_itemsがすでに複製であることから、イミュータブル用のgetItems() constの内容は戻り値の型にconstを加えるだけでよい。const修飾されたオブジェクトからはconst修飾されたメンバーにしかアクセスできないため、破壊的な操作はできなくなる。ただし、内部状態への参照を返すと、Cart型変数が破棄されたときにその参照も無効となってしまうため、扱いに注意が必要である。安全のため、イミュータブル用のgetItems() constの戻り値の型はstd::vector<T>として、ディープコピーを返す実装とすることもある（防衛的コピー）。
このC++の例はイミュータブル／ミュータブル兼用として作成されたものだが、コンストラクタについて2つのバージョンを用意する必要はないし、実際にできない。Cart型の変数を宣言するときにconstを付けるかどうかを決めるだけである。
なお、C++でも共有ポインタ（参照カウント方式のスマートポインタ）を使えば、Javaのようにコンストラクタ呼び出し元と状態を共有する設計にすることもできる。共有ポインタのコピーは、配列全体のコピーよりもコストがずっと小さいため、場合によってはあえてこちらのミュータブルな設計を選択することもある。
template<typename T>
class Cart {
private:
  std::shared_ptr<std::vector<T>> m_items;

public:
  explicit Cart(std::shared_ptr<std::vector<T>> items) : m_items(items) { }

  std::shared_ptr<std::vector<T>> getItems() { return m_items; }
  std::shared_ptr<const std::vector<T>> getItems() const { return m_items; }
  int total() const { /* return sum of the prices */ }
};
また、クラスが不変であるかどうかを確認する方法の一つとしてFindBugsというツールを使うという手がある。これはバグの温床になるコードを自動検出してくれて、不変クラスを作るときにも貢献する。