Identità e mutabilità dell'oggetto

8

Stavo leggendo una proposta per i tipi di valore in Java , e sono venuto attraverso questa frase: "L'identità dell'oggetto serve solo a supportare la mutabilità, dove lo stato di un oggetto può essere mutato ma rimane lo stesso oggetto intrinseco."

Da ciò che capisco (anche se provvisoriamente), l'identità dell'oggetto è l'idea della variabile che agisce come un puntatore o riferimento ad un oggetto situato altrove nella memoria (come gli oggetti istanziati sull'heap in Java o C #). Quindi cosa avrebbe a che fare con la mutabilità degli oggetti? Ciò implica che, ad esempio, gli oggetti istanziati nello stack in C ++ sono immutabili? Ho difficoltà a vedere il link qui.

    
posta Drazen Bjelovuk 04.05.2014 - 21:45
fonte

3 risposte

9

Prima di affrontare l'identità, definiamo ciò che intendiamo per uguaglianza un po 'più precisamente. Diciamo che due cose sono uguali se e solo se non possiamo distinguerle (vedi: Identity of indiscernibles ). Ciò significa che se due cose sono uguali o meno dipende dai mezzi che dobbiamo ispezionare.

Pensiamo ancora un po 'in termini di programmazione. Lasciamo i nostri preconcetti alla porta e supponiamo di lavorare in un linguaggio nuovo di zecca in cui tutte le variabili e i valori sono immutabili. Con la definizione di cui sopra, due valori A e B sono uguali se e solo se non ci sono programmi nella lingua che producono risultati diversi quando A è usato al posto di B o viceversa. Diciamo che A e B sono (IEEE 754) float e quando viene sostituito nell'espressione _ + 1.0 , il risultato è 1.0 per entrambi A e B . Sicuramente A e B sono entrambi zero. Sono uguali? Dipende - la lingua fornisce qualsiasi funzione che mi permetta di determinare il segno dello zero ? Se non lo fa, sono uguali; se lo fa, potrebbe non esserlo.

Quindi due valori sono uguali ogni volta che danno gli stessi risultati per tutte le possibili combinazioni di operazioni che supportano. I valori immutabili in particolare non producono risultati diversi a seconda di quali operazioni sono state precedentemente applicate a loro. Per questo motivo, non ci interessa se due variabili puntano a due copie dello stesso valore o se entrambe puntano alla stessa copia.

Che cosa ha a che fare con la mutabilità? La mutabilità implica che il nostro linguaggio abbia qualche nozione di una cella di memoria il cui contenuto può essere sovrascritto. Supponiamo che aggiungiamo il supporto per le celle di memoria mutabili nella nostra lingua:

  • ref <value> crea una nuova cella di memoria, distinta da tutte le altre, inizializzata su <value> .
  • <variable> := <value> sovrascrive il contenuto di una cella di riferimento.
  • !<variable> restituisce il valore attualmente memorizzato in una cella di riferimento.

Ora pensiamo a cosa significa l'uguaglianza per le celle di memoria. Supponi A = ref 0 e B = A . Considera questo programma:

A := 1
print(!_)

Sostituendo il bianco per A stampa 1 e sostituendo B stampa 1 . Supponiamo ora A = ref 0 e B = ref 0 . In questo caso, la sostituzione con il programma precedente stampa 1 e 0 , poiché ora A e B puntano a celle di memoria distinte.

Quindi è importante per noi se due riferimenti puntino alla stessa cella di memoria oa celle di memoria diverse. Dal momento che ciò conta, sarebbe utile avere un modo efficace e generale di distinguere due riferimenti. Il nostro metodo attuale per confrontare i valori che detengono, e se sono uguali, uno di loro è problematico per una serie di ragioni:

  • Dipende dall'essere in grado di confrontare i valori memorizzati nelle celle di memoria per l'uguaglianza. L'uguaglianza non ha senso per tutti i tipi - per esempio, generalmente non ha senso per le funzioni, perché non esiste un metodo generale per determinare se due funzioni sconosciute sono uguali (questo si sta avventurando nel territorio di Problema di Arresto). Quindi, dati due riferimenti alle celle di memoria che memorizzano funzioni, non possiamo confrontare le funzioni che mantengono per l'uguaglianza.
  • Dipende dall'avere un valore che possiamo assegnare a uno dei due riferimenti. Quindi, anche se l'uguaglianza avesse senso per tutti i tipi nella lingua, abbiamo ancora bisogno di accedere a un valore per ogni tipo che vogliamo confrontare. Cosa succede se la costruzione di un valore di quel tipo ha effetti collaterali?
  • Il valore di riferimento che usiamo per mutare uno dei riferimenti deve essere diverso dal valore che ha già la cella di memoria, quindi abbiamo effettivamente bisogno di due valori.
  • Il codice per confrontare i riferimenti di tipi diversi avrà esattamente lo stesso valore per i due valori che usiamo.
  • Abbiamo bisogno di eseguire il backup e ripristinare il valore del riferimento che mutiamo per evitare di cambiare il significato del programma.

Quindi sarebbe utile che il linguaggio fornisse un'operazione per controllare direttamente se due riferimenti puntano alla stessa cella di memoria mutevole. Tale funzione è inutile per valori immutabili; in effetti, direi che è decisamente dannoso. Se esistesse un modo per dire se due 1 s sono memorizzati in posti diversi nella memoria, allora ci possono essere programmi che si preoccupano se io passo a una 1 o l'altra. Non voglio davvero preoccuparmi se ho "il giusto 1 "; la matematica è già abbastanza difficile! Quindi è chiaro che essere in grado di verificare l'uguaglianza della memoria è utile soprattutto per i tipi mutabili.

    
risposta data 05.05.2014 - 15:15
fonte
5

Immagine hai due oggetti per esempio per le istanze della classe List . Entrambe le liste hanno lo stesso contenuto. Ora stai leggendo gli elenchi, calcolando e producendo qualcosa in base al loro contenuto. Importa quale lista usi? Non perché hanno lo stesso contenuto.

Ma cosa succede se uno degli elenchi è cambiato? Ora fa importa quale si sceglie per leggere e produrre qualcosa. Quindi devi essere in grado di distinguerli e vuoi anche in alcune situazioni essere in grado di controllare se due variabili puntano allo stesso oggetto (o all'elenco qui).

Se tuttavia non si è in grado di modificare il contenuto di un elenco, non è nemmeno necessario disporre di due oggetti elenco con lo stesso contenuto perché non è possibile modificarli comunque. Se si desidera "modificare" il contenuto, si creerà invece un nuovo oggetto elenco con contenuto diverso, che è come ho detto un nuovo oggetto. Quindi da questo punto di vista l'identità non ha importanza. Solo il contenuto fa e solo il contenuto dovrebbe essere usato per confrontare due liste.

Si noti inoltre che il compilatore potrebbe puntare allo stesso oggetto elenco anche se dichiari due oggetti, poiché deve solo memorizzarne il contenuto una volta che non può cambiare.

    
risposta data 04.05.2014 - 21:56
fonte
0

L'identità dell'oggetto non esiste "solo" per supportare la mutabilità, ma ha anche altri usi [infatti, un'istanza di% tipoObject è utile solo come un token di identità, poiché non ha attributi osservabili-mutevoli!] Invece, l'identità è una caratteristica spesso indesiderata che un oggetto mutevole acquisirà ogni volta che esistono più riferimenti all'oggetto, che non sono tutti controllati da un singolo proprietario e entità esterne al proprietario potrebbero-- senza le conoscenze del proprietario - fai o vedi le modifiche a quell'oggetto.

Supponiamo che qualche campo statico X contenga un riferimento all'istanza a elemento singolo di int[] e X [0] = 5. Il campo statico Y contiene anche un elemento per un'istanza a elemento singolo di int[] e Y [0] = 5. Se il codice chiama sempre IdentityHashCase() su X e Y , o se il codice prova X==Y , sarà in grado di distinguere se X e Y identificano la stessa istanza di int[] o diverse istanze. Allo stesso modo, se il codice fa qualcosa come X[0]+=1; Y[0]+=2; , il comportamento dipenderà dal fatto che X e Y identificano la stessa istanza o istanze diverse. Se, tuttavia, il codice non testa mai X e Y esplicitamente per l'uguaglianza di riferimento, né controlla il proprio codice hash dell'identità o fa qualcos'altro "riferimento-correlato" e se X[0] e Y[0] non vengono mai modificati, quindi X e Y saranno equivalenti indipendentemente dal fatto che identificano gli stessi matrici o matrici diverse.

Una delle cose più brutte dell'identità in Java o .NET è che l'identità di un oggetto dipende fondamentalmente dall'ubicazione di ogni entità nell'universo che potrebbe apportare o vedere cambiamenti a quell'oggetto. Se un riferimento a un oggetto è sempre esposto liberamente al codice esterno, il proprietario dell'oggetto perderà il controllo su di esso e non sarà mai in grado di recuperarlo.

I tipi di valore, a differenza degli oggetti, non possono mai essere osservati o modificati se non dall'oggetto o metodo in cui sono dichiarati. Quindi, un oggetto che contiene un campo di tipo valore non deve preoccuparsi di perdere il controllo su di esso, poiché è semanticamente impossibile che ciò accada. In .NET (anche se non in Java) è possibile passare una variabile, un campo, un elemento di matrice o un'altra posizione di memorizzazione come un "parametro di riferimento". In questo modo temporaneamente consente al metodo di osservare o modificare il contenuto del percorso di archiviazione passato per la durata della sua esecuzione, ma qualsiasi "passphr" passato (il termine tecnico per ciò che viene passato) è garantito per essere distrutto prima che il metodo ritorni, assicurando così che il chiamante mantenga il controllo sui dati in esso contenuti e assicurando che il contenitore per tali dati non acquisisca un'identità non desiderata .

    
risposta data 09.05.2014 - 19:56
fonte

Leggi altre domande sui tag