Quali sono le conseguenze delle classi immutabili con riferimenti a classi mutabili?

4

Recentemente ho iniziato ad adottare la migliore pratica di progettare le mie classi per essere immutabili per Effective Java [Bloch2008]. Ho una serie di domande interrelate sui gradi di mutabilità e le loro conseguenze.

Ho incontrato situazioni in cui una classe (Java) I implementata è solo "internamente immutabile" perché è immutabile, tranne che utilizza riferimenti (immutabili) ad altre classi mutabili. Per chiarire, se l'oggetto fosse costruito con i suoi riferimenti impostati su classi immutabili, la classe sarebbe immutabile.

  1. Alcuni dei benefici (vedi sotto) delle classi immutabili sono validi anche solo per le classi "internamente immutabili"?

  2. Esiste un termine accettato per il sopra menzionata "mutabilità interna"? oggetto immutabile di Wikipedia la pagina utilizza il termine non utilizzato "immutabilità profonda" per descrivere un oggetto i cui riferimenti sono anche immutabili.

  3. È la distinzione tra mutabilità e effetto collaterale / stato importante?

Il sito "Java Practices" elenca i seguenti vantaggi delle classi immutabili :

  • sono semplici da costruire, testare e usare

  • sono automaticamente thread-safe e non hanno problemi di sincronizzazione

  • non è necessario un costruttore di copia

  • non hanno bisogno di un'implementazione di clone

  • consente a hashCode di utilizzare lazy inizializzazione e per memorizzarne la cache valore di ritorno

  • non è necessario copiarlo in modo difensivo quando usato come campo

  • crea buoni tasti Mappa e Imposta elementi (questi oggetti non devono cambiare stato mentre nella raccolta)

  • hanno la loro invariante di classe stabilito una volta sulla costruzione, e non ha mai bisogno di essere controllato ancora una volta

  • hanno sempre "failure atomicity" (a termine usato da Joshua Bloch): se un oggetto immutabile lancia un'eccezione, non è mai stato lasciato in un modo indesiderabile o stato indeterminato

posta glenviewjeff 22.06.2011 - 20:33
fonte

5 risposte

6

Un Class non è veramente immutable se uno qualsiasi di questi riferimenti figli non è immutable . Se la tua radice Class ha riferimenti final a tutte le sue variabili di istanza e tutte quelle variabili di istanza sono immutable così come tutti i loro figli che seguono questa stessa limittion allora puoi diciamo che la radice Class è immutable .

Se uno qualsiasi dei riferimenti figli non è final o una delle istanze secondarie è mutabile , allora il contenuto di Class è non immutabile .

Non ha importanza sulla immutablità interna qualunque cosa significhi sia l'ipotesi di qualcuno, tutto ciò che conta è che il contratto pubblico alla classe immutabilità sia mantenuto. Non puoi avere un'immutabilità parziale più di quanto tu possa essere parzialmente morta. La tua classe è immutable o non lo è. E non lo è se qualcuno dei suoi riferimenti alle classi o alle loro classi, ecc. Sono mutabili .

Se hai un grafico e tutti i membri del grafico sono mutabili non puoi dire in modo deterministico che hai uno stato immutable . Le garanzie di concorrenza escono dalla finestra, .equals() e .hashCode() diventano non deterministiche e semplici test di clonazione e serializzazione escono anche dalla finestra.

Se qualcosa rompe questo contratto di immutabilità perdi tutti i vantaggi di provare a mantenere questo contratto di immutabilità.

I problemi di concorrenza più semplici sono la principale forza motivazionale dell'immutabilità.

Avere codice gratuito per effetti collaterali aiuta con prevedibilità e manutenibilità. Dal momento che non devi chiederti dove le cose vengono mutate nell'albero delle chiamate perché sai che non possono essere mutate.

Le prestazioni sono un altro fattore meno importante, in Java la JVM può prendere alcune decisioni altamente ottimizzate sulla cache e su altri fattori se sa che i dati non possono cambiare stato. Fornisce importanti suggerimenti al compilatore in fase di compilazione e gli algoritmi JIT in fase di runtime.

    
risposta data 22.06.2011 - 20:47
fonte
2

Non ha importanza finché la classe che dici non è modificabile. Ad esempio, la memo- rizzazione usa la mutazione, ma un valore memo- rizzato può essere considerato immutabile per tutti gli intenti.

Se non posso dire che il tuo oggetto usa internamente variabili mutabili, allora non comprometterà il mio programma e posso comporlo come al solito con altri oggetti.

    
risposta data 24.04.2013 - 16:28
fonte
0

L'immutabilità è utile quando si progettano applicazioni concorrenti a causa dello stato dell'oggetto che garantisce. È anche abbastanza utile per gli insiemi e come chiavi di mappa ... Per quanto mi riguarda non c'è scelta tra immutabilità e mutabilità. Ad esempio, in un'applicazione Bank con multithreading, un oggetto Account non può essere reso immutabile perché è un'entità dinamica che cambia gli straordinari e è univoco per un titolare. Tuttavia, uno sconto o un TAEG possono essere progettati come oggetti immutabili perché non variano oltre la definizione. In breve, il dominio del problema deciderà come modellare i tuoi oggetti. Troppe volte i programmatori Java scrivono il codice per "Gang Of Four" in contrasto con la codifica su richiesta del cliente.

    
risposta data 24.04.2013 - 08:23
fonte
0

Bene, il concetto sta leggendo il JLS e comprendendolo. In questo caso, la JLS dice:

I campi finali

consentono inoltre ai programmatori di implementare oggetti immutabili sicuri per i thread senza sincronizzazione. Un oggetto immutabile sicuro per i thread è considerato immutabile da tutti i thread, anche se viene utilizzata una sequenza di dati per passare i riferimenti all'oggetto immutabile tra i thread. Questo può fornire garanzie di sicurezza contro l'uso improprio di una classe immutabile da codice errato o malevolo. i campi finali devono essere usati correttamente per fornire una garanzia di immutabilità.

Il modello di utilizzo per i campi finali è semplice: imposta i campi finali per un oggetto nel costruttore dell'oggetto; e non scrivere un riferimento all'oggetto costruito in un punto in cui un altro thread può vederlo prima che il costruttore dell'oggetto sia finito. Se viene seguito, quando l'oggetto viene visto da un altro thread, quel thread vedrà sempre la versione correttamente costruita dei campi finali dell'oggetto. Vedrà anche le versioni di qualsiasi oggetto o array a cui fanno riferimento quei campi finali che sono almeno aggiornati come i campi finali. Quindi è necessario:

Crea indirizzi sia finali che privati. Evita che qualsiasi riferimento mutabile a quell'oggetto venga visto esternamente al tuo oggetto immutabile. In questo caso, # 2 probabilmente significa che non puoi restituire un riferimento a Indirizzo come hai con getAddress (). E devi fare una copia difensiva. Crea una copia dell'oggetto e memorizzala in Employee. Se non puoi fare una copia difensiva, non c'è davvero alcun modo per rendere Employee immutabile.

public final class Employee{
    private final int id;
    private final Address address;
    public Employee(int id, Address address)
    {
        this.id = id;
        this.address=new Address();  // defensive copy
        this.address.setStreet( address.getStreet() );
    }
    pulbic int getId(){
        return id;
    }
    public Address getAddress() {
        Address nuAdd = new Address(); // must copy here too
        nuAdd.setStreet( address.getStreet() );
        return nuAdd;
}

L'implementazione di clone () o qualcosa di simile (un copy ctor) renderebbe più facile la creazione di oggetti difensivi per classi complicate. Tuttavia, la migliore raccomandazione che penso sarebbe quella di rendere immutabile l'indirizzo. Una volta che lo fai puoi passare liberamente il suo riferimento senza problemi di sicurezza del thread.

In questo esempio, non è necessario copiare il valore di street. Street è una stringa e le stringhe sono immutabili. Se la strada fosse composta da campi mutabili (ad esempio il numero civico intero), allora dovrei fare anche una copia della strada, e così via all'infinito. Questo è il motivo per cui gli oggetti immutabili sono così preziosi che rompono la catena "copia infinita".

    
risposta data 12.09.2017 - 08:03
fonte
0

Bene, semplicemente messo; il tuo stato può cambiare ?

Questo non è solo attraverso l'API degli oggetti, ma in ogni modo un oggetto può cambiare stato.

  • Non puoi perdere riferimenti mutevoli.
  • Non puoi inserire riferimenti mutevoli.
  • Non puoi cambiare stato su alcun riferimento, nemmeno se l'oggetto è completamente autonomo.

È possibile inizializzare un oggetto mutabile, purché non vengano mai effettuate chiamate che cambiano stato, ma è comunque possibile interrogarlo. Quindi, se la mutabilità interna non è ciò che ho descritto qui, allora no, non puoi averlo.

    
risposta data 12.09.2017 - 08:22
fonte

Leggi altre domande sui tag