mutable
「mutable」の意味・「mutable」とは
「mutable」とは、変更可能な、または変わりやすいという意味を持つ英語の形容詞である。この単語は主に、物事が固定されていない状態や、変化に対応可能な性質を持つことを表現する際に用いられる。例えば、プログラミングの世界では、「mutable」は変数やオブジェクトが変更可能であることを示すために使われる。「mutable」の発音・読み方
「mutable」の発音は、IPA表記では/mjuːtəbəl/となる。IPAのカタカナ読みでは「ミュータブル」となる。日本人が発音するカタカナ英語では「ミュータブル」と読む。この単語は発音によって意味や品詞が変わる単語ではない。「mutable」の定義を英語で解説
「Mutable」 is an adjective in English that means capable of change or of being changed. It is often used to describe things that are not fixed and can adapt to changes. For example, in the world of programming, 'mutable' is used to indicate that a variable or object can be changed.「mutable」の類語
「mutable」の類語としては、「changeable」、「variable」、「alterable」などが挙げられる。これらの単語もまた、物事が変化可能であることを表す英語の形容詞である。「mutable」に関連する用語・表現
「mutable」に関連する用語としては、「immutable」がある。これは「mutable」の反対語で、変更不可能な、または変わりにくいという意味を持つ英語の形容詞である。「mutable」の例文
以下に、「mutable」を用いた例文を10個示す。 1. The mutable nature of fashion trends is what keeps the industry exciting.(ファッションのトレンドは変わりやすい性質があり、それが業界を刺激的にしている) 2. In Python, lists are mutable, but tuples are not.(Pythonでは、リストは変更可能(mutable)だが、タプルはそうではない) 3. The mutable weather made it difficult to plan outdoor activities.(変わりやすい天候のため、アウトドア活動の計画を立てるのが難しかった) 4. The mutable policies of the government have led to public confusion.(政府の変わりやすい政策により、公衆の混乱が生じている) 5. The mutable characteristics of the virus make it hard to develop a vaccine.(ウイルスの変わりやすい特性が、ワクチンの開発を難しくしている) 6. The mutable market conditions require businesses to be adaptable.(変わりやすい市場状況は、ビジネスに適応性を求める) 7. The mutable nature of language is what makes it so fascinating.(言語の変わりやすい性質が、それを魅力的にしている) 8. In Java, strings are immutable, not mutable.(Javaでは、文字列は不変(immutable)で、変更可能(mutable)ではない) 9. The mutable landscape of the digital world requires constant learning.(デジタル世界の変わりやすい風景は、絶えず学習を必要とする) 10. The mutable rules of the game made it more interesting.(ゲームの変わりやすいルールが、それをより面白くしている)イミュータブル
(ミュータブル から転送)
出典: フリー百科事典『ウィキペディア(Wikipedia)』 (2024/12/09 19:02 UTC 版)
コンピュータプログラミングにおいて、イミュータブル (英: immutable) なオブジェクトとは、作成後にその状態を変えることのできないオブジェクトのことである。対義語はミュータブル (英: mutable) なオブジェクトで、作成後も状態を変えることができる。mutableは「変更可能な」、immutableは「変更不可能な、不変の」という意味を持つ形容詞である。
あるオブジェクト全体がイミュータブルなこともあるし、C++でconst
データメンバを使う場合など、一部の属性のみがイミュータブルなこともある。場合によっては、内部で使われている属性が変化しても、外部からオブジェクトの状態が変化していないように見えるならば、オブジェクトをイミュータブルとみなすことがある。例えば、コストの高い計算の結果をキャッシュするためにメモ化を利用していても、そのオブジェクトは依然イミュータブルとみなせる。イミュータブルなオブジェクトの初期状態は大抵は生成時に設定されるが、オブジェクトが実際に使用されるまで遅らせることもある。
イミュータブルなオブジェクトを使うと、複製や比較のための操作を省けるため、コードが単純になり、また性能の改善にもつながる。しかしオブジェクトが変更可能なデータを多く持つ場合には、イミュータブル化は不適切となることが多い。このため、多くのプログラミング言語ではイミュータブルかミュータブルか選択できるようにしている。
背景
ほとんどのオブジェクト指向言語では、オブジェクトは参照の形でやり取りされる。Java・C++・Perl・Python・Rubyなどがその例である。この場合、オブジェクトが参照を通じて共有されていると、その状態が変更される可能性が問題となる。
もしオブジェクトがイミュータブルであったなら、オブジェクトの複製はオブジェクト全体の複製ではなく、単に参照の複製で済む。参照は通常オブジェクト自体よりもずっと小さいので(典型的にはポインタのサイズのみ)、メモリが節約でき、プログラムの実行速度もよくなる。
参照コピーのテクニックは、ミュータブルなオブジェクトに対して使用するのはさらに難しくなる。なぜならば、ミュータブルなオブジェクトの参照を保持する者が1人でもオブジェクトに変更を加えると、参照を共有する者全員がその影響を受けるからである。これが意図した作用でないならば、その他の参照の保持者に正しく対処してもらうよう通知するのは難しくなる可能性がある。このような場合、参照ではなくオブジェクト全体の防衛的(防御的)コピー (defensive copy) [1]を作成するのが一般的な解決法だが、これは簡単ではあるもののコストがかかる。他にはObserver パターンがミュータブルなオブジェクトへの変更に対処するのに利用できる。
イミュータブルなオブジェクトはマルチスレッドプログラミングにおいても有用となる。データがイミュータブルなオブジェクトで表現されていると、複数のスレッドが他のスレッドにデータを変更される心配なくデータにアクセスできる。つまり排他制御の必要がない。よってイミュータブルなオブジェクトのほうがミュータブルなものよりスレッドセーフであると考えられる。
等価なオブジェクトのコピーを作成する代わりに常に参照を複製するというテクニックはインターンとして知られる。インターンが使われているならば、2つのオブジェクトが等しいとみなされるのは、2つの参照が等しい場合でありかつその場合に限られる。いくつかの言語では自動的にインターンが行なわれる。例えばPythonでは文字列を自動的にインターンする。インターンを実装したアルゴリズムで、可能な場合は常にインターンすることが保証されているならば、オブジェクトの等価性の比較はそのポインタの比較に帰着し、多くのアプリケーションで高速化が達成できる。またアルゴリズムがそのような保証をしない場合でも、オブジェクトの比較の平均的なコストを下げることができる。一般的に、インターンの価値があるのはオブジェクトがイミュータブルなときだけである。
実装
![]() |
この節には、過剰に詳細な記述が含まれているおそれがあります。百科事典に相応しくない内容の増大は歓迎されません。
|
イミュータブルとは即ちオブジェクトがコンピュータのメモリ中で書き込み不可能であるという意味ではない。むしろイミュータブルはコンパイル時の問題であり、プログラマが「何をすべきか」であって、必ずしも「何ができるか」ではない。例えば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というツールを使うという手がある。これはバグの温床になるコードを自動検出してくれて、不変クラスを作るときにも貢献する。
関連項目
脚注
参考資料
外部リンク
英語
- Pattern: Immutable Object by Nat Pryce
- Java theory and practice: To mutate or not to mutate? by Brian Goetz
- Java Practices: Immutable objects
- Immutable Object - Portland Pattern Repository
- ミュータブルのページへのリンク