Classe immutabile?


Risposte:


136

Cos'è un oggetto immutabile?

Un oggetto immutabile è uno che non cambierà stato dopo che è stato istanziato.

Come rendere immutabile un oggetto?

In generale, un oggetto immutabile può essere creato definendo una classe che non ha nessuno dei suoi membri esposto e non ha setter.

La seguente classe creerà un oggetto immutabile:

class ImmutableInt {
  private final int value;

  public ImmutableInt(int i) {
    value = i;
  }

  public int getValue() {
    return value;
  }
}

Come si può vedere nell'esempio sopra, il valore di ImmutableIntpuò essere impostato solo quando l'oggetto è istanziato, e avendo solo un getter ( getValue) lo stato dell'oggetto non può essere cambiato dopo l'istanza.

Tuttavia, è necessario prestare attenzione che anche tutti gli oggetti a cui fa riferimento l'oggetto debbano essere immutabili, altrimenti potrebbe essere possibile modificare lo stato dell'oggetto.

Ad esempio, consentire un riferimento a un array o ArrayListessere ottenuto tramite un getter consentirà allo stato interno di cambiare cambiando l'array o la raccolta:

class NotQuiteImmutableList<T> {
  private final List<T> list;

  public NotQuiteImmutableList(List<T> list) {
    // creates a new ArrayList and keeps a reference to it.
    this.list = new ArrayList(list); 
  }

  public List<T> getList() {
    return list;
  }
}

Il problema con il codice di cui sopra è che ArrayListpuò essere ottenuto getListe manipolato, portando lo stato dell'oggetto stesso ad essere alterato, quindi non immutabile.

// notQuiteImmutableList contains "a", "b", "c"
List<String> notQuiteImmutableList= new NotQuiteImmutableList(Arrays.asList("a", "b", "c"));

// now the list contains "a", "b", "c", "d" -- this list is mutable.
notQuiteImmutableList.getList().add("d");

Un modo per aggirare questo problema è restituire una copia di un array o di una raccolta quando viene chiamato da un getter:

public List<T> getList() {
  // return a copy of the list so the internal state cannot be altered
  return new ArrayList(list);
}

Qual è il vantaggio dell'immutabilità?

Il vantaggio dell'immutabilità deriva dalla concorrenza. È difficile mantenere la correttezza negli oggetti mutabili, poiché più thread potrebbero tentare di cambiare lo stato dello stesso oggetto, portando alcuni thread a vedere uno stato diverso dello stesso oggetto, a seconda della tempistica delle letture e delle scritture su detto oggetto.

Avendo un oggetto immutabile, è possibile garantire che tutti i thread che stanno guardando l'oggetto vedranno lo stesso stato, poiché lo stato di un oggetto immutabile non cambierà.


5
La thread safety (importante per la concorrenza) non è l'unico vantaggio dell'immutabilità; significa anche che non devi fare copie difensive degli oggetti e previene i bug perché non puoi per errore modificare oggetti che non dovrebbero essere modificati.
Jesper

7
Invece di restituire una copia dell'elenco, puoi anche farlo return Collections.unmodifiableList(list); restituire una visualizzazione di sola lettura nell'elenco.
Jesper

19
Anche la lezione deve essere fatta final. Altrimenti può essere esteso con un metodo setter (o altri tipi di metodi mutanti e campi mutabili).
Abhinav Sarkar

6
@AbhinavSarkar Non deve esserlo finalse la classe ha solo privatecampi poiché non è possibile accedervi dalle sottoclassi.
icza

3
La classe @icza deve essere definitiva, perché abbiamo un metodo getter pubblico, possiamo estendere la classe e sovrascrivere il metodo getter e quindi cambiare il campo a modo nostro .. quindi non è più immutabile
sunil

19

Oltre alle risposte già fornite, consiglierei di leggere sull'immutabilità in Effective Java, 2a edizione, poiché ci sono alcuni dettagli che sono facili da perdere (ad esempio copie difensive). Inoltre, Efficace Java 2nd Ed. è una lettura obbligata per ogni sviluppatore Java.


3
Questa è la risorsa esatta da guardare. I vantaggi menzionati vanno dal codice meno soggetto a errori alla sicurezza dei thread.
gpampara

6

Rendi una classe immutabile come questa:

public final class Immutable
{
    private final String name;

    public Immutable(String name) 
    {
        this.name = name;
    }

    public String getName() { return this.name; } 

    // No setter;
}

Di seguito sono riportati i requisiti per rendere immutabile una classe Java:

  • La classe deve essere dichiarata come final(in modo che le classi figlie non possano essere create)
  • I membri della classe devono essere dichiarati come final(in modo che non sia possibile modificarne il valore dopo la creazione dell'oggetto)
  • Scrivi metodi Getter per tutte le variabili in esso contenute per ottenere i valori dei membri
  • Nessun metodo Setter

Le classi immutabili sono utili perché
: sono thread-safe.
- Esprimono anche qualcosa di profondo sul tuo design: "Non posso cambiare questo"., Quando si applica, è esattamente ciò di cui hai bisogno.


5

L'immutabilità può essere ottenuta principalmente in due modi:

  • utilizzando finalattributi di istanza per evitare la riassegnazione
  • usando un'interfaccia di classe che semplicemente non consente alcuna operazione che sia in grado di modificare ciò che è all'interno della tua classe (solo getter e nessun setter

I vantaggi dell'immutabilità sono i presupposti che puoi fare su questi oggetti:

  • ottieni la regola senza effetto collaterale (che è molto popolare nei linguaggi di programmazione funzionale) e ti permette di usare gli oggetti in un ambiente simultaneo più facilmente, poiché sai che non possono essere modificati in modo atomico o non atomico quando sono utilizzato da molti thread
  • le implementazioni dei linguaggi possono trattare questi oggetti in modo diverso, collocandoli in zone di memoria che vengono utilizzate per i dati statici, consentendo un utilizzo più veloce e sicuro di questi oggetti (questo è ciò che accade all'interno della JVM per le stringhe)

Immutabile non è la stessa cosa senza effetti collaterali uguali. Ad esempio, un oggetto immutabile potrebbe produrre effetti collaterali come la registrazione in un file. È leggermente impreciso dire che rendere un oggetto immutabile lo rende anche privo di effetti collaterali.
Grundlefleck

1
@ Grundleflek, penso che questo potrebbe essere un problema. La classe non è immutabile se modifica un file di registro come parte del contratto e tale file di registro è accessibile ad altre classi. Se il file di registro è nascosto da altre classi e non fa parte del contratto della classe, la classe è effettivamente immutabile e in realtà a tutti gli effetti priva di effetti collaterali. L'introduzione (non fornita) sulla pagina degli effetti collaterali di Wikipedia recita "... si dice che un'espressione abbia un effetto collaterale se, oltre a produrre un valore, modifica anche uno stato o ha un'interazione osservabile con le funzioni chiamanti".
Jeff Axelrod

3

Le classi immutabili non possono riassegnare i valori dopo che sono state istanziate. Il costruttore assegna i valori alle sue variabili private. Fino a quando l'oggetto non diventa nullo, i valori non possono essere modificati a causa dell'indisponibilità dei metodi setter.

essere immutabile dovrebbe soddisfare i seguenti,

  • Tutte le variabili dovrebbero essere private .
  • Non vengono forniti metodi mutatori (setter).
  • Evita l'override del metodo rendendo la classe finale (Strong Immutability) o i metodi finali (Week immutability).
  • Clona profondamente se contiene classi non primitive o mutabili.

/**
* Strong immutability - by making class final
*/
public final class TestImmutablity {

// make the variables private
private String Name;

//assign value when the object created
public TestImmutablity(String name) {
this.Name = name;
}

//provide getters to access values
public String getName() {

return this.Name;
}
}

Vantaggi: gli oggetti immutabili contengono i suoi valori inizializzati finché non muore.

java-immutable-classes-short-note


2

Le classi immutabili sono quelle i cui oggetti non possono essere modificati dopo la creazione.

Le classi immutabili sono utili per

  • Scopo della memorizzazione nella cache
  • Ambiente concorrente (ThreadSafe)
  • Difficile per l'eredità
  • Il valore non può essere modificato in nessun ambiente

Esempio

String Class

Esempio di codice

public final class Student {
    private final String name;
    private final String rollNumber;

    public Student(String name, String rollNumber) {
        this.name = name;
        this.rollNumber = rollNumber;
    }

    public String getName() {
        return this.name;
    }

    public String getRollNumber() {
        return this.rollNumber;
    }
}

2

Come si può rendere immutabile una classe Java?

Da JDK 14+ che ha JEP 359 , possiamo usare " records". È il modo più semplice e veloce per creare una classe Immutabile.

Una classe record è un vettore trasparente e poco modificabile per un insieme fisso di campi noto come record componentsche fornisce una statedescrizione per il record. Ciascuno componentdà origine a un finalcampo che contiene il valore fornito e unaccessor metodo per recuperare il valore. Il nome del campo e il nome dell'accessorio corrispondono al nome del componente.

Consideriamo l'esempio della creazione di un rettangolo immutabile

record Rectangle(double length, double width) {}

Non è necessario dichiarare alcun costruttore, non è necessario implementare metodi uguali e hashCode. Qualsiasi record richiede un nome e una descrizione dello stato.

var rectangle = new Rectangle(7.1, 8.9);
System.out.print(rectangle.length()); // prints 7.1

Se vuoi convalidare il valore durante la creazione dell'oggetto, dobbiamo dichiarare esplicitamente il costruttore.

public Rectangle {

    if (length <= 0.0) {
      throw new IllegalArgumentException();
    }
  }

Il corpo del record può dichiarare metodi statici, campi statici, inizializzatori statici, costruttori, metodi di istanza e tipi annidati.

Metodi di istanza

record Rectangle(double length, double width) {

  public double area() {
    return this.length * this.width;
  }
}

campi statici, metodi

Poiché lo stato dovrebbe far parte dei componenti, non possiamo aggiungere campi istanza ai record. Tuttavia, possiamo aggiungere campi e metodi statici:

record Rectangle(double length, double width) {

  static double aStaticField;

  static void aStaticMethod() {
    System.out.println("Hello Static");
  }
}

qual è il bisogno di immutabilità e c'è qualche vantaggio nell'usarlo?

Le risposte pubblicate in precedenza sono abbastanza buone da giustificare la necessità di immutabilità e sono vantaggiose


0

Un altro modo per creare oggetti immutabili è usare la libreria Immutables.org :

Supponendo che siano state aggiunte le dipendenze richieste, creare una classe astratta con metodi di accesso astratti. Puoi fare lo stesso annotando con interfacce o anche annotazioni (@interface):

package info.sample;

import java.util.List;
import java.util.Set;
import org.immutables.value.Value;

@Value.Immutable
public abstract class FoobarValue {
  public abstract int foo();
  public abstract String bar();
  public abstract List<Integer> buz();
  public abstract Set<Long> crux();
}

È ora possibile generare e quindi utilizzare l'implementazione immutabile generata:

package info.sample;

import java.util.List;

public class FoobarValueMain {
  public static void main(String... args) {
    FoobarValue value = ImmutableFoobarValue.builder()
        .foo(2)
        .bar("Bar")
        .addBuz(1, 3, 4)
        .build(); // FoobarValue{foo=2, bar=Bar, buz=[1, 3, 4], crux={}}

    int foo = value.foo(); // 2

    List<Integer> buz = value.buz(); // ImmutableList.of(1, 3, 4)
  }
}

0

Una classe immutabile è semplicemente una classe le cui istanze non possono essere modificate.

Tutte le informazioni contenute in ogni istanza sono fissate per la durata dell'oggetto, quindi non è mai possibile osservare modifiche.

Le classi immutabili sono più facili da progettare, implementare e utilizzare rispetto alle classi modificabili.

Per rendere una classe immutabile, segui queste cinque regole:

  1. Non fornire metodi che modificano lo stato dell'oggetto

  2. Assicurati che la classe non possa essere estesa.

  3. Rendi definitivi tutti i campi.

  4. Rendi privati ​​tutti i campi.

  5. Garantire l'accesso esclusivo a qualsiasi componente modificabile.

Gli oggetti immutabili sono intrinsecamente thread-safe; non richiedono sincronizzazione.

Gli oggetti immutabili possono essere condivisi liberamente.

Gli oggetti immutabili sono ottimi elementi costitutivi per altri oggetti


0

@ Jack, avere campi finali e setter in classe non renderà la classe immutabile. la parola chiave finale si assicura solo che una variabile non venga mai riassegnata. È necessario restituire una copia completa di tutti i campi nei metodi getter. Questo farà in modo che, dopo aver ottenuto un oggetto dal metodo getter, lo stato interno dell'oggetto non venga disturbato.


-1

In qualità di non madrelingua inglese, non mi piace la comune interpretazione di "classe immutabile" che significa "oggetti di classe costruiti sono immutabili"; piuttosto, io stesso propendo di interpretarlo come "l'oggetto classe stesso è immutabile".

Detto questo, "classe immutabile" è una sorta di oggetto immutabile. La differenza è quando si risponde qual è il vantaggio. A mia conoscenza / interpretazione, la classe immutabile impedisce ai suoi oggetti di modificare il comportamento in fase di esecuzione.


-1

La maggior parte delle risposte qui sono buone e alcune hanno menzionato le regole, ma ritengo che sia utile esprimere a parole perché e quando dobbiamo seguire queste regole. Quindi sto dando la spiegazione di seguito

  • Dichiarare le variabili membro come "finali": quando le dichiariamo come finali, il compilatore ci obbliga a inizializzarle. Possiamo inizializzare direttamente, per default, dal costruttore arg. (Vedi il codice di esempio sotto) e dopo l'inizializzazione non possiamo modificarli poiché sono definitivi.
  • E ovviamente se proviamo a usare i setter per quelle variabili finali, il compilatore genera un errore.

    public class ImmutableClassExplored {
    
        public final int a; 
        public final int b;
    
        /* OR  
        Generally we declare all properties as private, but declaring them as public 
        will not cause any issues in our scenario if we make them final     
        public final int a = 109;
        public final int b = 189;
    
         */
        ImmutableClassExplored(){
            this. a = 111;
            this.b = 222;
        }
    
        ImmutableClassExplored(int a, int b){
            this.a = a;
            this.b= b;
        }
    }
    

Dobbiamo dichiarare la classe come "finale"?

  • Senza la parola chiave finale nella dichiarazione della classe, la classe può essere ereditata. Quindi la sottoclasse può sovrascrivere i metodi getter. Qui dobbiamo considerare due scenari:

1. Avere solo membri primitivi: non abbiamo problemi Se la classe ha solo membri primitivi, non è necessario dichiarare la classe come finale.

2. Avere oggetti come variabili membro: se abbiamo oggetti come variabili membro, dobbiamo rendere definitivi anche i membri di quegli oggetti. Significa che dobbiamo attraversare in profondità l'albero e rendere tutti gli oggetti / primitive come finali, cosa che potrebbe non essere possibile tutte le volte. Quindi la soluzione alternativa è rendere la classe finale che impedisce l'ereditarietà. Quindi non si tratta di sottoclasse che sovrascrivono i metodi getter.


-1

L'annotazione @Value di Lombok può essere utilizzata per generare classi immutabili. È semplice come il codice qui sotto.

@Value
public class LombokImmutable {
    int id;
    String name;
}

Come da documentazione sul sito di Lombok:

@Value è la variante immutabile di @Data; tutti i campi vengono resi privati ​​e definitivi per impostazione predefinita e i setter non vengono generati. Anche la classe stessa viene resa finale per impostazione predefinita, perché l'immutabilità non è qualcosa che può essere forzato in una sottoclasse. Come @Data, vengono generati anche metodi utili per String (), equals () e hashCode (), ogni campo ottiene un metodo getter e viene generato anche un costruttore che copre ogni argomento (eccetto i campi finali che vengono inizializzati nella dichiarazione del campo) .

Un esempio completamente funzionante può essere trovato qui.


Prima di rispondere a una vecchia domanda che ha una risposta accettata (cerca il verde ✓) e altre risposte assicurati che la tua risposta aggiunga qualcosa di nuovo o sia altrimenti utile in relazione ad esse. Si prega di fare attenzione quando si risponde alla domanda di OP Come si può rendere immutabile una classe Java, qual è la necessità dell'immutabilità e c'è qualche vantaggio nell'usarla? . - Stai dando solo una risposta parziale che afferma solo che si può usare Lombok, un framework / libreria di terze parti, che non è necessariamente l'argomento della domanda di OP. Anche Java 15 è uscito, perché usare Lombok quando è recordpossibile utilizzare Java ?
Ivo Mori

Sto dando una delle opzioni su come ottenere questo risultato. Non tutti usano Java 15, la maggior parte delle applicazioni oggi gira su versioni precedenti, quindi dici perché usare Lombok non ha molto senso. Nel mio progetto attuale siamo recentemente migrati a Java 11 e stiamo usando Lombok per ottenere classi immutabili. Inoltre, la mia risposta è un'aggiunta a quelle che erano già presenti. Ciò che è immutabile ha già ricevuto una risposta, immagino che ti aspetti che lo riscriva solo per il completamento.
Anubhav

Giusto. Non ho votato né a favore né a favore. Ho cercato di sottolineare i motivi per cui altre due persone hanno votato contro questa risposta. Sta a te decidere se e come modificare la tua risposta.
Ivo Mori
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.