2013-03-07 15 views
16

This Java tutorial dice che un oggetto immutabile non può cambiare il suo stato dopo la creazione.È corretto chiamare java.lang.String immutabile?

java.lang.String ha un campo

/** Cache the hash code for the string */ 
private int hash; // Default to 0 

che viene inizializzato sulla prima chiamata del metodo hashCode(), quindi cambia dopo la creazione:

String s = new String(new char[] {' '}); 
    Field hash = s.getClass().getDeclaredField("hash"); 
    hash.setAccessible(true); 
    System.out.println(hash.get(s)); 
    s.hashCode(); 
    System.out.println(hash.get(s)); 

uscita

0 
32 

Va corretto chiamare String immutabile?

+17

Gli hack di riflessione non contano contro l'immutabilità. – Perception

+0

http://stackoverflow.com/q/11146255/758280 – Jeffrey

+0

Come dice @Perception, gli hack di riflessione non dovrebbero contare. La memorizzazione nella cache del valore di hash in un campo privato non influisce su alcun metodo o stato non privato. –

risposta

8

Il termine "Immutabile" è abbastanza vago da non consentire una definizione precisa.

Suggerisco di leggere Kinds of Immutability dal blog di Eric Lippert. Sebbene sia tecnicamente un articolo in C#, è abbastanza pertinente alla domanda posta. In particolare:

osservazionale immutabilità:

Supponiamo che hai un oggetto che ha la proprietà che ogni volta si chiama un metodo su di esso, guarda un campo, ecc, si ottiene il stesso risultato . Dal punto di vista del chiamante tale oggetto sarebbe immutabile . Tuttavia, è possibile immaginare che dietro le quinte l'oggetto esegua un'inizializzazione pigra, memoizzando i risultati delle chiamate di funzione nella tabella di hash , ecc. Le "viscere" dell'oggetto potrebbero essere completamente modificabili.

Che importa? Gli oggetti veramente immutabili non cambiano mai il loro stato interno e sono quindi intrinsecamente sicuri. Un oggetto che è mutabile dietro le quinte potrebbe ancora aver bisogno del codice di threading complicato per proteggere il suo stato interno mutabile dalla corruzione se l'oggetto fosse chiamato su due thread "allo allo stesso tempo".

+0

Immagino che, a meno che non siate assoluti, la questione dell '"immutabilità" dipende dalla prospettiva di chi (cioè di quale oggetto) viene chiesta. – ArtB

0

Non può essere modificato dall'esterno ed è una classe finale, quindi non può essere sottoclasse e reso mutabile. Sono due requisiti per l'immutabilità. La riflessione è considerata un hack, non è un modo normale di sviluppo.

0

Reflection consente di modificare il contenuto di qualsiasi campo privato. È quindi corretto chiamare qualsiasi oggetto in Java immutabile?

L'immutabilità si riferisce a modifiche avviate o percepibili dall'applicazione.

In caso di stringa, il fatto che una particolare implementazione scelga di calcolare pigramente l'hashcode non è percepibile dall'applicazione. Farei un ulteriore passo avanti e dire che una variabile interna che viene incrementata dall'oggetto - ma mai esposta e mai usata in altro modo - sarebbe accettabile anche in un oggetto "immutabile".

+1

In realtà ho usato un metodo pubblico per cambiare lo stato della stringa –

+0

@EvgeniyDorofeev - non nell'esempio che hai mostrato. Almeno, non un metodo pubblico della classe * String *. Se esiste un metodo pubblico della classe * String * che consente di modificare il suo stato, allora sono d'accordo: non è immutabile. Ma non conosco alcun metodo del genere. – parsifal

+0

ma ho chiamato il metodo pubblico hashCode() nell'esempio e il campo hash è cambiato. –

0

Una classe può essere immutabile pur avendo campi mutabili, purché non fornisca l'accesso ai suoi campi mutabili.

È immutabile per progettazione. Se si utilizza Reflection (ottenendo il campo dichiarato e ripristinando la sua accessibilità), si sta aggirando il suo design.

1

Sì, è corretto chiamarli immutabili.

Se è vero che si può raggiungere in e modificare private ... e final ... le variabili di una classe, è una cosa inutile e incredibilmente poco saggio fare su un oggetto String. In genere è assunto che nessuno sarà abbastanza pazzo da farlo.

Da un punto di vista della sicurezza, le chiamate di riflessione necessarie per modificare lo stato di una stringa eseguono tutti i controlli di sicurezza. A meno che tu non abbia implementato la sandbox, le chiamate verranno bloccate per codice non attendibile. Quindi dovresti preoccuparti di questo come un modo in cui il codice non affidabile può rompere la sicurezza sandbox.

Vale anche la pena notare che il JLS afferma che l'uso del riflesso per modificare final, può rompere gli elementi (ad esempio nel multi-threading) o potrebbe non avere alcun effetto.

+0

Ho usato il metodo pubblico hashCode() per modificare il campo hash. I documenti dicono che gli oggetti immutabili non possono essere dichiarati dopo la creazione. La parte del campo hash di uno stato di istanza di String o no? –

+0

@EvgeniyDorofeev: Con alcune eccezioni (come i metodi 'delay'), il fatto che un metodo richieda del tempo per essere eseguito non è considerato parte del suo comportamento. L'unica differenza osservabile tra un oggetto 'stringa' il cui campo hash è zero e uno il cui campo hash non è, sarà la quantità di tempo richiesta per eseguire il suo metodo' hashCode() '. Si noti che se il valore dell'hash calcolato dipendesse in qualche modo quando è stata eseguita la prima chiamata a 'hashCode()', * rappresenterebbe * lo stato mutabile, ma non lo è. – supercat

+0

@EvgeniyDorofeev - Nel caso si parli di ('String'), la risposta dipende dalla tua prospettiva. Guarda la risposta di Ani. Ricordare inoltre che Java Tutorial non è la specifica. È una versione semplificata delle informazioni in (più) documenti definitivi. Se si guarda a JLS, * l'immutabilità * non è una proprietà principale. Piuttosto è una proprietà di una classe che emerge dal modo in cui la classe API progetta e la sua implementazione. Lo scopo principale di Java Tutorial è aiutare i principianti ... a non essere un testo definitivo. –

3

Una volta creati, tutti i metodi su un'istanza String (chiamati con gli stessi parametri) forniranno sempre lo stesso risultato. Non puoi cambiare il suo comportamento (con qualsiasi metodo pubblico), quindi rappresenterà sempre la stessa entità. Inoltre è final e non può essere sottoclasse, quindi è garantito che tutte le istanze si comporteranno in questo modo.

Pertanto dalla vista pubblica l'oggetto è considerato immutabile. In questo caso lo stato interno non ha molta importanza.

+0

Ah sì, ma in questo caso sta usando trucchi subdoli per cambiare la variabile 'hash', e la variabile' hash' è osservabile tramite 'hashcode()'. Quindi, secondo la tua definizione, String sarebbe mutabile. –

+0

Non ho ripetuto il primo commento che * il riflesso non conta * anche nella mia definizione. – gaborsch

13

Una definizione migliore sarebbe non che l'oggetto non cambiamento, ma che non può essere osservato a sono stati cambiati. Il suo comportamento non cambierà mai: .substring(x,y) restituirà sempre la stessa cosa per quella stringa idem per equals e tutti gli altri metodi.

Quella variabile viene calcolata la prima volta che si chiama .hashcode() e viene memorizzata nella cache per ulteriori chiamate. Questo è in pratica quello che chiamano "memoization" nei linguaggi di programmazione funzionale.

Reflection non è realmente uno strumento per "programmare", ma piuttosto per la meta-programmazione (cioè programmi di programmazione per generare programmi) quindi non conta davvero. È l'equivalente di cambiare il valore di una costante usando un debugger di memoria.

0

Sì, è corretto. Quando hai modificato una stringa come fai nel tuo esempio, viene creata una nuova stringa, ma la precedente mantiene il suo valore.

1

Dal punto di vista di uno sviluppatore che utilizza la riflessione, è non corretto per chiamare String immutabile. Ci sono veri sviluppatori Java che usano la riflessione per scrivere software reale ogni giorno. Eliminare il riflesso come "hack" è assurdo. Tuttavia, dal punto di vista di uno sviluppatore che non usa la riflessione, dal, è corretto chiamare String immutabile. Indipendentemente dal fatto che sia valido presumere che String sia immutabile dipende dal contesto.

L'immutabilità è un concetto astratto e pertanto non può essere applicato in senso assoluto a qualsiasi cosa che abbia una forma fisica (vedere lo ship of Theseus). I costrutti del linguaggio di programmazione come oggetti, variabili e metodi esistono fisicamente come bit in un supporto di memorizzazione. La degradazione dei dati è un processo fisico che accade a tutti i supporti di memorizzazione, quindi non si può mai dire che i dati siano veramente immutabili. Inoltre, è quasi sempre possibile in pratica sovvertire le caratteristiche del linguaggio di programmazione volte a prevenire la mutazione di un dato particolare. Al contrario, il numero 3 è 3, è sempre stato 3, e sarà sempre 3.

Come applicato programmare dati, immutability dovrebbe essere considerata una utile assunzione piuttosto che una proprietà fondamentale. Ad esempio, se si presuppone che uno String è immutabile, è possibile memorizzare nella cache il proprio codice hash per riutilizzarlo ed evitare il costo di dover ricalcolare il suo codice hash in un secondo momento. Praticamente tutto il software non banale si basa su ipotesi che alcuni dati non muteranno per determinate durate di tempo. Gli sviluppatori di software generalmente presuppongono che lo code segment di un programma non cambierà mentre è in esecuzione, a meno che non stiano scrivendo codice auto-modificante. Capire quali presupposti sono validi in un particolare contesto è un aspetto importante dello sviluppo del software.

Problemi correlati