String è una classe immutabile in Java. Una classe immutabile è semplicemente una classe le cui istanze non possono essere modificate. Perché il linguaggio di programmazione Java sceglie di rendere immutabili gli oggetti della classe String?
String è una classe immutabile in Java. Una classe immutabile è semplicemente una classe le cui istanze non possono essere modificate. Perché il linguaggio di programmazione Java sceglie di rendere immutabili gli oggetti della classe String?
Risposte:
Questo problema è fortemente connesso al concetto di cosa significhi essere un'istanza di una classe. In termini strettamente orientati agli oggetti, una classe ha un invariante associato: un predicato che vale sempre all'uscita da un metodo (pubblico) della classe. Tale nozione è fondamentale per garantire che l'eredità sia ben definita, ad esempio (fa parte del principio di sostituzione di Liskov ).
Uno dei problemi più perniciosi con Java è che è difficile impedire al codice client di infrangere gli invarianti di classe.
Ad esempio, considera la seguente classe 'ZipCode':
class ZipCode {
private String zipCode;
public ZipCode(String value){
if(!isValidZipCode(value))
throw new IllegalArgumentException();
zipCode = value;
assert(invariant());
}
public String get() { return zipCode; }
public boolean invariant() {
return isValidZipCode( zipCode );
}
}
Se String non fosse immutabile, sarebbe possibile per un utente di ZipCode chiamare 'get' e cambiare i caratteri in qualsiasi momento successivo, rompendo così l'invariante e distruggendo l'integrità concettuale offerta dall'incapsulamento del concetto ZipCode.
Dal momento che questo tipo di integrità è essenziale per garantire la validità dei sistemi di grandi dimensioni, questa risposta alla tua domanda implora davvero quella più ampia di:
"Perché Java non supporta un analogo della const C ++, o almeno offre versioni immutabili di più delle sue classi di libreria?"
Cose come stringhe e date sono naturalmente valori. In termini di C ++, ci aspettiamo che abbiano un costruttore di copie, un operatore di assegnazione e un operatore di uguaglianza, ma non ci aspettiamo mai di prendere il loro indirizzo. Pertanto, non prevediamo che vengano allocati individualmente nell'heap. I metodi virtuali non hanno senso.
Gli oggetti di dominio sono naturalmente riferimenti. Quelli C ++ non hanno costruttore di copie, operatore di assegnazione o operatore di uguaglianza (sono uguali solo se identici). Possiamo prendere il loro indirizzo e ci aspettiamo che vengano assegnati heap. I metodi sono generalmente virtuali.
Java non ha classi di valore, solo quelle di riferimento. I valori sono falsificati con oggetti immutabili. Questo è vero per le stringhe, ma non, sfortunatamente, per le date. La mutabilità delle date di Java ha causato problemi frequenti e ora è deprecata. I valori mutabili non possono essere utilizzati come base per un hash, ad esempio.
Java è stato progettato per consentire l'esecuzione di sottosezioni del codice di un programma in ambienti con limitazioni di sicurezza. Il modo in cui questo requisito è stato implementato è stato impostando un "SecurityManager" su un thread che ha accesso ai parametri di determinate operazioni critiche (ad es. Apertura di un file) e ha chiesto se si potesse consentire o meno all'operazione di procedere. Se le stringhe Java fossero mutabili, un programma potrebbe eludere tali restrizioni creando due thread, uno che eseguiva un'operazione di file aperto che sarebbe consentita, mentre l'altro modificava la stringa in cui memorizzava il nome del file in uno che non era consentito. Esiste quindi la possibilità che il responsabile della sicurezza legga la stringa originale, accetti l'operazione, che verrebbe trasferita al codice di apertura del file che precederebbe l'apertura del secondo file (non consentito).
Quest'ultima possibilità farebbe funzionare tutte queste operazioni più lentamente e sarebbe più probabile che l'implementazione contenesse dei bug, quindi l'uso di stringhe immutabili è stata la decisione più sensata.
Più in generale, gli oggetti immutabili sono utili perché consentono la condivisione senza la necessità di effettuare copie difensive (che potrebbe essere necessario anche nel codice non critico per la sicurezza per prevenire bug quando i dati di origine cambiano), quindi anche senza questo requisito la decisione sarebbe comunque ragionevole.