web-dev-qa-db-ja.com

オブジェクトの同一性と可変性

私は Javaの値型の提案 を読んでいて、この文に出くわしました。「オブジェクトIDは、オブジェクトの状態を変更できるが、同じ組み込みオブジェクトのままである、可変性をサポートするためだけに機能します。」

(仮にではありますが)私が理解していることから、オブジェクトIDは、変数がメモリ内の別の場所にあるオブジェクト(JavaまたはC#)では、これはオブジェクトの可変性とどう関係しているのでしょうか?これは、たとえば、C++でスタックにインスタンス化されたオブジェクトが不変であることを意味しますか?ここのリンクが表示されません。

8
Drazen Bjelovuk

アイデンティティに取り組む前に、equalityの意味をもう少し正確に定義しましょう。区別できない場合に限り、2つのものは等しいと言います(参照: 識別不能の識別情報 )。つまり、2つのものが等しいかどうかは、検査する手段に依存します。

それをプログラミング用語でもう少し考えてみましょう。私たちの先入観を扉に残して、すべての変数と値が不変であるまったく新しい未知の言語で作業しているとしましょう。上記の定義では、Aの代わりにBを使用した場合、またはその逆の場合に異なる結果を生成するプログラムが言語にない場合に限り、2つの値ABは等しくなります。 ABが(IEEE 754)floatであり、式_ + 1.0に代入すると、ABの両方の結果は1.0になります。確かにABはどちらもゼロです。彼らは等しいですか?それは依存します-言語は私が決定することを可能にする任意の関数を提供します ゼロの符号 ?そうでない場合、それらは同等です。もしそうなら、そうではないかもしれません。

したがって、2つの値は、サポートする操作のすべての可能な組み合わせに対して同じ結果を与えるときは常に等しいです。特に不変値は、以前にそれらに適用された操作に応じて、異なる結果を生成しません。そのため、2つの変数が同じ値の2つのコピーを指しているのか、または両方が同じコピーを指しているのかは関係ありません。

これは、可変性とどのような関係がありますか?変異性は、私たちの言語には、内容を上書きできるメモリセルの概念があることを意味します。言語に可変メモリセルのサポートを追加するとします。

  • ref <value>は、他のすべてとは異なり、<value>に初期化されたnewメモリセルを作成します。
  • <variable> := <value>は、参照セルの内容を上書きします。
  • !<variable>は、参照セルに現在格納されている値を返します。

次に、メモリセルの平等とはどういう意味かを考えてみましょう。 A = ref 0B = Aがあるとします。このプログラムを考えてみましょう:

A := 1
print(!_)

Aを空白に置き換えると1が出力され、Bを代入すると1も出力されます。次に、A = ref 0およびB = ref 0を想定します。この場合、上記のプログラムに代入すると10が出力されます。これは、ABが異なるメモリセルを指しているためです。

したがって、2つの参照が同じメモリセルを指しているのか、異なるメモリセルを指しているのかが重要です。それが重要なので、2つの参照を区別する効率的で一般的な方法があると便利です。それらが保持する値を比較する現在の方法、およびそれらが等しい場合、それらの1つを変更することは、いくつかの理由で厄介です。

  • これは、メモリセルに格納されている値を比較できるかどうかに依存します。 2つの未知の関数が等しいかどうかを判別する一般的な方法がないため(たとえば、これはホールティング問題領域に進出しています)、すべてのタイプに対して平等は意味を成しません。したがって、関数を格納するメモリセルへの2つの参照が与えられた場合、それらが保持する関数を等しいかどうか比較することはできません。
  • それは、2つの参照のうちの1つに割り当てることができる値があるかどうかに依存します。そのため、言語のすべての型で等価性が理にかなっている場合でも、比較する各型の値にアクセスする必要があります。そのタイプの値を作成すると副作用がある場合はどうなりますか?
  • 参照の1つを変更するために使用する参照値は、メモリセルがすでに持っている値とは異なる必要があるため、実際には2つの値が必要です。
  • 異なるタイプの参照を比較するコードは、使用する2つの値を保存した場合とまったく同じように見えます。
  • プログラムの意味が変更されないように、変更した参照の値をバックアップして復元する必要があります。

したがって、2つの参照が同じ可変メモリセルを指しているかどうかを直接チェックする操作を言語が提供することは有用です。このような関数は、不変の値には意味がありません。実際、それは実に有害だと思います。 2つの1sがメモリ内の異なる場所に格納されているかどうかを確認する方法が存在する場合、1のどちらを渡すかを気にするプログラムが存在する可能性があります。 「正しい1」を持っているかどうかは心配したくない。数学はそのままで十分難しいです!したがって、メモリの等価性をチェックできることは、主に変更可能な型に役立つことは明らかです。

10
Doval

クラスListのインスタンスなど、2つのオブジェクトがあるイメージ。どちらのリストも同じ内容です。今、あなたはリストを読んでいて、それらの内容に基づいて何かを計算して出力しています。どのリストを使用するかは重要ですか?同じ内容ではないからです。

しかし、リストの1つが変更された場合はどうなりますか?さて、それは行う何かを読み、出力するためにどれを選択するかが重要です。したがって、それらを区別できるようにする必要があり、状況によっては、2つの変数が同じオブジェクト(またはここのリスト)を指しているかどうかを確認できるようにする必要があります。

ただし、リストの内容を変更できない場合は、いずれにしても変更できないため、同じ内容の2つのリストオブジェクトを用意する必要はありません。コンテンツを「変更」したい場合は、代わりに異なるコンテンツを持つ新しいリストオブジェクトを作成します。これは、私が新しいオブジェクトと言ったとおりです。したがって、この観点からは、アイデンティティは重要ではありません。 2つのリストの比較には、コンテンツのみが使用され、コンテンツのみが使用されます。

また、2つのオブジェクトを宣言しても、コンパイラーは同じリストオブジェクトをポイントする可能性があることにも注意してください。

5
valenterry

オブジェクトIDは、可変性をサポートするためだけに存在するわけではありませんが、他の用途もあります[実際、Object型のインスタンスは、識別可能な可変属性がないため、IDトークンとしてのみ役立ちます!]代わりに、アイデンティティはしばしば不必要特性であり、可変オブジェクトはオブジェクトへの複数の参照が存在するときにいつでも取得し、それらはすべて単一の所有者によって制御されているわけではなく、所有者以外のエンティティは-所有者の知らないうちに、そのオブジェクトを変更または変更します。

いくつかの静的フィールドXが_int[]_の単一要素インスタンスへの参照を保持し、X [0] = 5であるとします。静的フィールドYは、_int[]_の単一要素インスタンスへの要素も保持し、Y [0] = 5です。コードがXおよびYに対してIdentityHashCase()を呼び出す場合、またはコードが_X==Y_をテストする場合、Xかどうかを識別できます。 Yは、_int[]_の同じインスタンスまたは異なるインスタンスを識別します。同様に、コードが_X[0]+=1; Y[0]+=2;_のようなものを実行する場合、動作はXYが同じインスタンスを識別するか、異なるインスタンスを識別するかによって異なります。ただし、コードがXYを明示的に参照の等価性についてテストしたり、IDハッシュコードを確認したり、その他の「参照関連」を実行したりしない場合、および_X[0]_および_Y[0]_は変更されないため、XYは、同じ配列を識別するか、異なる配列を識別するかに関係なく、同等になります。

Javaまたは.NETでのIDの厄介な点の1つは、オブジェクトのIDは基本的に、そのオブジェクトに変更を加えるか、または表示する可能性がある宇宙内のすべてのエンティティの所在に依存することです。オブジェクトへの参照は外部コードに自由に公開されます。オブジェクトの所有者はそれを制御できなくなり、元に戻すことはできません。

値型は、オブジェクトとは異なり、宣言されているオブジェクトまたはメソッド以外では監視または変更できません。したがって、値タイプのフィールドを保持するオブジェクトは、それを制御できなくなることを心配する必要はありません。それが発生することは意味的に不可能だからです。 .NET(Javaではありません)では、変数、フィールド、配列要素、またはその他の格納場所を「参照パラメーター」として渡すことができます。そうすることで一時的にメソッドは、実行中に渡された保管場所の内容を監視または変更できますが、渡された「byref」(渡されたものの専門用語)は、メソッドが戻る前に破棄されるため、呼び出し元が保持しているデータを確実に制御し、そのデータのコンテナがnwanted IDを取得しないようにします。

0
supercat