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


8

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

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


「これは、たとえば、C ++でスタック上にインスタンス化されたオブジェクトが不変であることを意味します」オブジェクトを不変にするのは、インスタンス化された場所やIDではなく、オブジェクトがどのように設計され、どのように許可されるかです。どう思いますか?
ジョーダン2014年

その提案に関連するディスカッションフォーラムはありますか?値型は、ダクトテープで結合された変数の集まりと見なすことをお勧めします。与えられたstruct Point {public int x,y;}、宣言Point ptはと呼ばれる2つの変数を効果的に宣言する必要がpt.xありpt.yます。タイプのメソッドパラメータはPoint2つのintパラメータである必要があります。このようなルールの下では、ランタイムで変更が必要になるのは配列と関数の戻り値だけです。プリミティブのみまたはオブジェクトのみを含む配列を処理できます...
supercat

...プリミティブ配列またはオブジェクト配列とインターリーブ要素を使用する。プリミティブと値の混合を含む配列は、オブジェクトの配列として収容でき、その最初の項目は値の配列への参照でした。構造内のオブジェクトまたはプリミティブの数が制限されている場合、Threadそれらを保持するメンバーを追加することにより、ランタイムを変更せずに関数の戻りを処理できます。
スーパーキャット2014

1
@supercatはJCPが機能する方法を示しています。誰かがコア言語でそれを望んでいて、「Javaがこれを取得しないと死んでいる」と大声で叫ぶと、JCPが含まれます。
2014年

この記事は、次のテーマについて役立ちます。オブジェクトは不変である必要がある
yegor256

回答:


10

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

それをプログラミング用語でもう少し考えてみましょう。私たちの先入観をドアに残して、すべての変数と値が不変であるまったく新しい未知の言語で作業しているとしましょう。上記の定義では、の代わりに使用した場合、またはその逆の場合に異なる結果を生成するプログラムが言語にない場合にのみ、2つの値ABは等しくなります。さんが言ってみましょうと浮かぶ、および式に代入するとき(IEEE 754)され、結果は両方のためにと。確かにと両方ともゼロです。それらは等しいですか?それは依存します-言語は私がゼロのサインを決定することを可能にするあらゆる機能を提供しますかABAB_ + 1.01.0ABAB?そうでない場合、それらは同等です。もしそうなら、そうではないかもしれません。

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

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

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

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

A := 1
print(!_)

ブランクをAプリント1に置き換え、Bプリント1も同様に置き換えます。今、と仮定A = ref 0B = ref 0ます。この場合、上記のプログラムプリントに代入1し、0今から、AおよびBポイント異なるメモリセルへ。

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

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

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


5

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

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

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

また、2つのオブジェクトを宣言しても、コンテンツは変更できないため、コンテンツを1度保存するだけでよいため、コンパイラが同じリストオブジェクトを指す可能性があることにも注意してください。


0

オブジェクトIDがサポート無常に「のみ」が存在するが、同様に他の用途を持っていない[確かに、のインスタンスObjectには一切observably-可変属性を持っているので、タイプは、唯一のアイデンティティトークンとして有用である!代わりに、アイデンティティがあることが多いです-可変オブジェクトがオブジェクトへの複数の参照が存在する場合はいつでも取得する不要な特性、それらはすべて単一の所有者によって制御されるわけではありません。 。

いくつかの静的フィールドXがの単一要素インスタンスへの参照を保持し、int[]X [0] = 5であるとします。静的フィールドYには、の単一要素インスタンスへの要素も含まれ、int[]Y [0] = 5です。コードIdentityHashCase()Xandを呼び出す場合Y、またはコードがtestの場合、同じインスタンスか異なるインスタンスX==YXY識別して識別できint[]ます。同様に、コードがのようなものを実行するX[0]+=1; Y[0]+=2;場合、動作は同じインスタンスまたは異なるインスタンスXY識別するかどうかに依存します。ただし、コードが参照の等価性をテストXしてY明示的に確認したり、IDハッシュコードを確認したりしない場合、または「参照関連」の他の処理を行う場合X[0]、およびY[0]次いで、修飾されたことはないXYにかかわらず、それらが同じ配列または異なる配列を識別するかどうかと同等であろう。

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

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


JavaではObject、非常に重要な可変属性、関連する相互排除があります!
Jan Hudec、2014年

@JanHudec:ObjectIDトークンとして使用する場合、Javaと.NETのロックを監視すると思います。それらがオブジェクトの可変状態をカプセル化していると見なされる限り、不変オブジェクトなどはありません。皮肉なことに、より重要な設計目標が単一の参照型を使用してランタイムを簡略化することであったため、Javaが「教育言語」と見なされることもあります。意味論的な観点からは、アイデンティティを持つタイプとアイデンティティを持たないタイプを区別する方が良いでしょう。不変型の主な利点は...
スーパーキャット2014年

...同じ状態をカプセル化する不変のインスタンスは交換可能に使用できますが、型がそれを保証する方法はありません。コードがnew String("Hello");のキーとして使用される場合IdentityHashMap、文字列は、それらの文字をそのマップを知らないコードにカプセル化する他の文字列のように見えますが、その基礎となるオブジェクトには次のようなIDトークンがあるため、置換できません。宇宙全体で他のすべてのオブジェクトとは異なります。
スーパーキャット2014年

ロックが技術的に含まれているのか、外部データ構造に格納されているのかは関係ありません。意味的には、オブジェクトの識別を可能にする可変プロパティとして動作します。そして、ロックが明確に含まれているJavaオブジェクトの低レベルの説明を見たことはほぼ間違いありません。Javaのメモリ効率が極端に低い理由の1つ。
Jan Hudec、2014年

@JanHudec:意味的には、より不変になるオブジェクトはありませんObject。たとえばによって満たされるクラスレベルの不変性の定義は、によってIntegerも等しく満たされObjectます。さらに、一般に、オブジェクトが可変状態をカプセル化するといわれる場合、その状態をあるインスタンスから別のインスタンスに意味のある形でコピーすることが可能です。オブジェクトのロック状態を別のオブジェクトにコピーする方法はないと思います。
スーパーキャット2014年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.