Objektidentität und Veränderlichkeit

8

Ich habe einen Vorschlag für Werttypen in Java gelesen und bin auf diesen Satz gestoßen: "Die Objektidentität dient nur zur Unterstützung der Veränderlichkeit, wobei der Status eines Objekts mutiert werden kann, aber dasselbe intrinsische Objekt bleibt."

Soweit ich weiß (wenn auch nur vorläufig), ist Objektidentität die Idee, dass Ihre Variable als Zeiger oder Verweis auf ein Objekt fungiert, das sich an einer anderen Stelle im Speicher befindet (z. B. Objekte, die auf dem Heap in Java oder C # instanziiert wurden). Was hätte das mit Objektveränderlichkeit zu tun? Bedeutet dies, dass beispielsweise instanziierte Objekte auf dem Stapel in C ++ unveränderlich sind? Ich habe Probleme, den Link hier zu sehen.

Drazen Bjelovuk
quelle
"Bedeutet dies, dass beispielsweise instanziierte Objekte auf dem Stapel in C ++ unveränderlich sind?" Ich denke, was ein Objekt unveränderlich macht, ist, wie das Objekt entworfen wurde und was es zulässt, nicht, wo es instanziiert wird oder welche Identität es hat. Was denken Sie?
Jordan
Ist mit diesem Vorschlag ein Diskussionsforum verbunden? Ich würde vorschlagen, dass ein Wertetyp als eine Reihe von Variablen betrachtet wird, die mit Klebeband zusammengebunden sind. In struct Point {public int x,y;}einer Deklaration Point ptsollten zwei Variablen mit dem Namen pt.xund deklariert werden pt.y. Ein Methodenparameter vom Typ Pointsollte zwei intParameter sein. Unter solchen Regeln wären die einzigen Aspekte, die eine Änderung der Laufzeit erfordern würden, Arrays und Funktionsrückgabewerte. Arrays, die nur
Grundelemente
... durch Verwendung primitiver Arrays oder Objektarrays und Interleaving-Elemente. Arrays, die eine Mischung aus Grundelementen und Werten enthalten, könnten als Array von Objekten untergebracht werden, deren erstes Element eine Referenz auf ein Array von Werten war. Wenn die Anzahl der Objekte oder Grundelemente in einer Struktur begrenzt war, konnten Funktionsrückgaben ohne Änderungen an der Laufzeit verarbeitet werden, indem Mitglieder hinzugefügt wurden Thread, um sie zu halten.
Superkatze
1
@supercat so wie das JCP funktioniert, wenn jemand es in der Kernsprache will und laut genug schreit, dass "Java ist tot, wenn es das nicht bekommt", wird es aufgenommen.
Jwenting
Sie können diesen Artikel hilfreich finden, zu diesem Thema: Objekte sollten unveränderlich sein
yegor256

Antworten:

10

Bevor wir uns mit Identität befassen, definieren wir etwas genauer , was wir unter Gleichheit verstehen . Wir sagen, zwei Dinge sind genau dann gleich, wenn wir sie nicht unterscheiden können (siehe: Identität von Ununterscheidbaren ). Das heißt, ob zwei Dinge gleich sind oder nicht, hängt davon ab, wie wir sie untersuchen müssen.

Lassen Sie uns noch etwas mehr über die Programmierung nachdenken. Lassen wir unsere Vorurteile an der Tür und nehmen an, wir arbeiten in einer brandneuen unbekannten Sprache, in der alle Variablen und Werte unveränderlich sind. Nach der obigen Definition sind zwei Werte Aund Bgenau dann gleich, wenn es KEINE Programme in der Sprache gibt, die unterschiedliche Ergebnisse liefern, wenn Asie anstelle von Boder umgekehrt verwendet werden. Lassen Sie uns sagen Aund Bsind (IEEE 754) schwimmt, und wenn in den Ausdruck _ + 1.0, ist das Ergebnis 1.0für beide Aund B. Sicher Aund Bsind beide Null. Sind sie gleich? Das hängt davon ab, ob die Sprache eine Funktion bietet, mit der ich das Vorzeichen der Null bestimmen kann? Wenn nicht, sind sie gleich; Wenn ja, können sie nicht sein.

Daher sind zwei Werte jedes Mal gleich, wenn sie für alle möglichen Kombinationen von Operationen, die sie unterstützen, dieselben Ergebnisse liefern. Insbesondere unveränderliche Werte führen nicht zu unterschiedlichen Ergebnissen, je nachdem, welche Operationen zuvor auf sie angewendet wurden. Aus diesem Grund ist es uns egal, ob zwei Variablen auf zwei Kopien desselben Werts verweisen oder ob beide auf dieselbe Kopie verweisen.

Was hat das mit Veränderlichkeit zu tun? Veränderlichkeit impliziert, dass unsere Sprache eine Vorstellung von einer Speicherzelle hat, deren Inhalt überschrieben werden kann. Nehmen wir an, wir unterstützen unsere Sprache um veränderbare Speicherzellen:

  • ref <value>erstellt eine neue Speicherzelle, die sich von allen anderen unterscheidet und auf initialisiert ist <value>.
  • <variable> := <value> überschreibt den Inhalt einer Referenzzelle.
  • !<variable> Gibt den aktuell in einer Referenzzelle gespeicherten Wert zurück.

Lassen Sie uns nun darüber nachdenken, was Gleichheit für Speicherzellen bedeutet. Angenommen, A = ref 0und B = A. Betrachten Sie dieses Programm:

A := 1
print(!_)

Ersetzen Sie ADrucke durch den Rohling 1und ersetzen Sie auch BDrucke 1. Nehmen wir nun an A = ref 0und B = ref 0. In diesem Fall Einsetzen in die obigen Programm druckt 1und 0, da jetzt Aund Bzeigen Sie auf verschiedene Speicherzellen.

Es ist uns also wichtig, ob zwei Referenzen auf dieselbe Speicherzelle oder auf unterschiedliche Speicherzellen verweisen. Da dies wichtig ist, wäre es nützlich, zwei Referenzen effizient und allgemein voneinander zu unterscheiden. Unsere derzeitige Methode zum Vergleichen der Werte, die sie enthalten, und wenn sie gleich mutieren, ist einer von ihnen aus mehreren Gründen problematisch:

  • Es hängt davon ab, ob die in den Speicherzellen gespeicherten Werte auf Gleichheit verglichen werden können. Gleichheit ist nicht für alle Typen sinnvoll - zum Beispiel ist sie für Funktionen im Allgemeinen bedeutungslos, da es keine allgemeine Methode gibt, um festzustellen, ob zwei unbekannte Funktionen gleich sind (dies wagt sich in das Gebiet des Halteproblems). Angesichts zweier Verweise auf Speicherzellen, in denen Funktionen gespeichert sind, können wir die Funktionen, die sie für die Gleichheit enthalten, nicht vergleichen.
  • Es hängt davon ab, welchen Wert wir einer der beiden Referenzen zuweisen können. Selbst wenn Gleichheit für alle Typen in der Sprache sinnvoll ist, benötigen wir dennoch Zugriff auf einen Wert für jeden Typ, den wir vergleichen möchten. Was ist, wenn die Erstellung eines solchen Werts Nebenwirkungen hat?
  • Der Referenzwert, den wir zum Mutieren einer der Referenzen verwenden, muss sich von dem Wert unterscheiden, den die Speicherzelle bereits hat, sodass wir tatsächlich zwei Werte benötigen.
  • Der Code zum Vergleichen von Referenzen verschiedener Typen sieht bis auf die beiden von uns verwendeten Werte genau gleich aus.
  • Wir müssen den Wert der Referenz, die wir mutieren, sichern und wiederherstellen, um zu vermeiden, dass sich die Bedeutung des Programms ändert.

Daher wäre es für die Sprache nützlich, eine Operation bereitzustellen, mit der direkt überprüft werden kann, ob zwei Referenzen auf dieselbe veränderbare Speicherzelle verweisen. Eine solche Funktion ist für unveränderliche Werte sinnlos; in der Tat würde ich sagen, es ist geradezu schädlich. Wenn es eine Möglichkeit 1gab, festzustellen , ob zwei s an verschiedenen Stellen im Speicher gespeichert sind, kann es Programme geben, die sich darum kümmern, ob ich das eine 1oder das andere übergebe. Ich möchte mir wirklich keine Sorgen machen, ob ich "das Richtige 1" habe; Mathe ist schon schwer genug! Es ist also klar, dass die Überprüfung der Speichergleichheit hauptsächlich für veränderbare Typen nützlich ist.

Doval
quelle
5

Bild Sie haben zwei Objekte zum Beispiel für Instanzen der Klasse List. Beide Listen haben den gleichen Inhalt. Jetzt lesen Sie die Listen und berechnen und geben etwas basierend auf ihrem Inhalt aus. Ist es wichtig, welche Liste Sie verwenden? Es liegt nicht daran, dass sie den gleichen Inhalt haben.

Aber was ist, wenn eine der Listen geändert wird? Jetzt ist es tut unabhängig davon , welche Sie sich entscheiden für das Lesen und outputing etwas. Daher müssen Sie in der Lage sein, zwischen ihnen zu unterscheiden, und Sie möchten auch in bestimmten Situationen überprüfen können, ob zwei Variablen auf dasselbe Objekt (oder eine Liste hier) verweisen.

Wenn Sie jedoch den Inhalt einer Liste nicht ändern können, müssen Sie nicht einmal zwei Listenobjekte mit demselben Inhalt haben, da Sie sie sowieso nicht ändern können. Wenn Sie den Inhalt "ändern" möchten, erstellen Sie stattdessen ein neues Listenobjekt mit anderem Inhalt - wie gesagt ein neues Objekt. Unter diesem Gesichtspunkt spielt die Identität also keine Rolle. Nur der Inhalt funktioniert und nur der Inhalt sollte zum Vergleichen von zwei Listen verwendet werden.

Beachten Sie auch, dass der Compiler auf dasselbe Listenobjekt verweisen kann, selbst wenn Sie zwei Objekte deklarieren, da er seinen Inhalt nur einmal speichern muss, da er sich nicht ändern kann.

Valenterry
quelle
0

Die Objektidentität existiert nicht "nur", um die Veränderlichkeit zu unterstützen, sondern hat auch andere Verwendungszwecke [tatsächlich ist eine Instanz des ObjectTyps nur als Identitätstoken nützlich, da sie keinerlei beobachtbar veränderbare Attribute hat!] Stattdessen ist Identität häufig - Unerwünschte Eigenschaft, die ein veränderliches Objekt erhält, wenn mehrere Verweise auf das Objekt vorhanden sind. Sie werden nicht alle von einem einzelnen Eigentümer gesteuert, und Entitäten außerhalb des Eigentümers können - ohne Wissen des Eigentümers - Änderungen an diesem Objekt vornehmen oder sehen .

Angenommen, ein statisches Feld Xenthält einen Verweis auf eine Einzelelementinstanz von int[]und X [0] = 5. Das statische Feld enthält Yauch ein Element für eine Einzelelementinstanz von int[]und Y [0] = 5. Wenn Code jemals ruft IdentityHashCase()an Xund Y, oder , wenn Code - Tests X==Y, wird es in der Lage sein , ob zu erkennen Xund Ydie gleiche Instanz zu identifizieren int[]oder verschiedene Instanzen. Ebenso, wenn Code so etwas wie tut X[0]+=1; Y[0]+=2;, wird das Verhalten davon abhängen , ob Xund Ydie gleiche Instanz oder verschiedene Instanzen identifizieren. Wenn der Code jedoch niemals die Referenzgleichheit prüft Xund Yexplizit überprüft oder seinen Identitäts-Hash-Code überprüft oder irgendetwas anderes "referenzbezogen" tut, und wenn X[0]undY[0]werden dann nie geändert Xund Ysind gleichwertig, unabhängig davon, ob sie dieselben oder unterschiedliche Arrays identifizieren.

Eines der unangenehmen Dinge an der Identität in Java oder .NET ist, dass die Identität eines Objekts im Wesentlichen vom Aufenthaltsort jeder Entität im Universum abhängt, die möglicherweise Änderungen an diesem Objekt vornimmt oder sieht. Wenn ein Verweis auf ein Objekt jemals frei externem Code ausgesetzt ist, verliert der Eigentümer des Objekts die Kontrolle darüber und kann es niemals zurückerhalten.

Werttypen können im Gegensatz zu Objekten nur durch das Objekt oder die Methode, in der sie deklariert sind, beobachtet oder geändert werden. Ein Objekt, das ein Feld vom Werttyp enthält, muss sich also keine Sorgen machen, die Kontrolle darüber zu verlieren, da dies semantisch unmöglich ist. In .NET (wenn auch nicht in Java) ist es möglich, eine Variable, ein Feld, ein Array-Element oder einen anderen Speicherort als "Referenzparameter" zu übergeben. Dadurch wird vorübergehend die Methode zu beobachten oder zu ändern , den Inhalt des übergebenen Lagerort für die Dauer seiner Ausführung erlauben, aber jede bestanden „byref“ (der Fachbegriff für das, was passiert ist) gewährleistet ist, bevor die Methode zurückkehrt , zerstört werden, Dadurch wird sichergestellt, dass der Anrufer die Kontrolle über die darin gespeicherten Daten behält.unerwünschte Identität.

Superkatze
quelle
ObjectHat in Java ein sehr wichtiges veränderliches Attribut, den damit verbundenen gegenseitigen Ausschluss!
Jan Hudec
@ JanHudec: Ich würde sagen, Monitorsperren in Java und .NET werden als ObjectIdentitätstoken verwendet. In dem Maße, wie sie als Kapselung eines veränderlichen Zustands in einem Objekt angesehen werden, gibt es kein unveränderliches Objekt. Es ist ironisch, dass Java manchmal als "Unterrichtssprache" angesehen wird, da ein wichtigeres Entwurfsziel darin bestand, die Laufzeit durch Verwendung eines einzigen Referenztyps zu vereinfachen. Aus semantischer Sicht wäre es besser gewesen, Typen mit einer Identität von solchen ohne Identität zu unterscheiden. Der
Hauptvorteil
... dass unveränderliche Instanzen, die denselben Status kapseln, austauschbar verwendet werden können, aber ein Typ kann dies auf keinen Fall garantieren. Wenn Code new String("Hello");als Schlüssel in a verwendet wird IdentityHashMap, sieht die Zeichenfolge wie jede andere Zeichenfolge aus, die diese Zeichen in Code einkapselt, der diese Zuordnung nicht kennt, sie kann jedoch nicht ersetzt werden, da das zugrunde liegende Objekt über ein Identitätstoken verfügt unterscheidet sich von jedem anderen Objekt im gesamten Universum.
Supercat
Es spielt keine Rolle, ob die Sperre technisch enthalten oder in einer externen Datenstruktur gespeichert ist. Semantisch verhält sie sich wie eine veränderbare Eigenschaft, mit der die Identität des Objekts unterschieden werden kann. Und ich bin mir fast sicher, dass ich die Beschreibung von Java Object auf niedriger Ebene gesehen habe, in der die Sperre eindeutig enthalten war. Einer der Gründe für die extreme Speicherineffizienz von Java.
Jan Hudec
@ JanHudec: Semantisch kann kein Objekt unveränderlicher sein als Object. Jede Definition der Unveränderlichkeit auf Klassenebene, die z. B. erfüllt Integerwürde, würde gleichermaßen erfüllt sein durch Object. Wenn ein Objekt einen veränderlichen Zustand einkapseln soll, ist es im Allgemeinen möglich, diesen Zustand sinnvoll von einer Instanz in eine andere zu kopieren. Ich glaube nicht, dass es eine Möglichkeit gibt, den Sperrstatus eines Objekts in ein anderes zu kopieren.
Supercat