不変なオブジェクト

不変、immutable について。生成したら生涯、値が変わらないオブジェクトを不変なオブジェクトといいます。
string や Uri クラスなどが例です。誰もが使っている割りに、言及されることはまれです。string や Uri と対になる mutable なクラスは StringBuilder と UriBuilder ですね。

書籍なら「Effective Java」、結城浩さんの「Java言語で学ぶ デザインパターン入門 マルチスレッド編」に説明があります。詳しい解説はこれらを参照してください。私も不変初心者なので(^^;

利点を Effective Java から引用すると、「設計、実装、使用が可変クラスよりも容易」、「誤りにくく、より安全」。でも、設計、実装が容易という点はそうとも言えないかも。wikipedia 参照。これは簡単とは言えない…。単純な値を表すクラスを不変にするのは簡単かもしれないけど、複雑なクラスを不変にするのは大変、ということかもしれません。
ほかの利点は、スレッドセーフである(同期不要)、共有できる、さらに進めてインターン化できる(値の種類が少ないならとても強力かもしれない)、などなど。Effective Java では複素数型を例に、関数的だと説明しています。副作用がないってことですね。
不変な型は使う分には間違いなく簡単です。中途半端な状態がないのは気持ちいいし、何も考えなくてもスレッドセーフなのもいい感じ。
欠点は異なる値を表すのに別のオブジェクトが必要なる点だそうで、特にオブジェクトが大きいとちょっとの変更でも大量のコピーが必要になって、マズー。

外から見て不変であればいいので、内部に可変の値を持つのはありです。C++ の mutable キーワードを検索すれば、このような例が見つかります。mutable ってこのためにあったんだねぇ。


Effective Java には「可変にすべきかなり正当な理由がない限り、クラスは不変であるべきです。」と強烈なことが書いてあります。今後、検討してみる。


で、前にも書いたけど気になったきっかけはこれ。C#3.0の匿名型が immutable に変更になった。これはあるべき姿かもしれない。
オブジェクトの生存期間の途中で、ハッシュ値が変わることがマズイというわけで immutable にしたようです。しかし、Paul Vickさんはこれは匿名型だけの問題じゃないよね、と指摘Orcas 以降でほかの型もハッシュ値の求め方を変更しようと考えているようです。これまでハッシュ値をどうやって求めるか、たびたび悩んでたのでちょっと期待。ハッシュ値ってのは今も盛んに研究されてますよね。主に暗号関連のほうかもしれないけど。
Dictionary<,> の MSDN ライブラリの説明に「オブジェクトが Dictionary のキーとして使用されている場合、そのオブジェクトに対してハッシュ値に影響するような変更を行わないでください。」とあります。これまではもう変更しないオブジェクトを Dictionary に入れてたけど、そういうものを不変にすればよいわけですね。一発で作れない場合は、対応する mutable なクラスを用意すればOKです。

もう一つの気になったきっかけ。演算子 == のオーバーライドに、「型が変更できない場合、つまりインスタンスに含まれているデータを変更できない場合、その型は、変更不可能なオブジェクトとして、同じ値を持つ限り同一と見なされるので、参照の等価の代わりに値の等価を比較するように演算子 == をオーバーロードするのが有効です。変更不可能な型以外で演算子 == をオーバーライドすることはお勧めしません。」とあります。
なんで「変更不可能な型以外で演算子 == をオーバーライドすることはお勧めしません。」なのか理由がわかりません…。わかりませんが、値を表す型は不変にするべき → 値を表す型以外なら参照の等価(同一性)を調べるだけで十分 って意味なのかなぁと推測。どなたか理由がわかる方、教えてください。こうじゃないの?ってご意見も歓迎。


でも複数の型からなる型を不変にするのって、大変そう。匿名型も自身だけが不変でも、含んでいる型が不変じゃなければ変更できるんじゃないのかな?気になる。


というわけで、これからは不変性を気にしてみることにします。Effective Java、良い本ですよ、オススメです。