Classe immuable?

Réponses:

135

Qu'est-ce qu'un objet immuable?

Un objet immuable est celui qui ne changera pas d'état après avoir été instancié.

Comment rendre un objet immuable?

En général, un objet immuable peut être créé en définissant une classe dont aucun de ses membres n'est exposé et qui n'a pas de setters.

La classe suivante créera un objet immuable:

class ImmutableInt {
  private final int value;

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

  public int getValue() {
    return value;
  }
}

Comme on peut le voir dans l'exemple ci-dessus, la valeur de ImmutableIntne peut être définie que lorsque l'objet est instancié, et en n'ayant qu'un getter ( getValue), l'état de l'objet ne peut pas être modifié après l'instanciation.

Cependant, il faut veiller à ce que tous les objets référencés par l'objet soient également immuables, sinon il pourrait être possible de changer l'état de l'objet.

Par exemple, autoriser une référence à un tableau ou ArrayListêtre obtenue via un getter permettra à l'état interne de changer en modifiant le tableau ou la collection:

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;
  }
}

Le problème avec le code ci-dessus est que le ArrayListpeut être obtenu à travers getListet être manipulé, ce qui conduit à modifier l'état de l'objet lui-même, donc pas immuable.

// 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");

Une façon de contourner ce problème consiste à renvoyer une copie d'un tableau ou d'une collection lorsqu'elle est appelée à partir d'un getter:

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

Quel est l'avantage de l'immuabilité?

L'avantage de l'immuabilité vient avec la concurrence. Il est difficile de maintenir l'exactitude des objets mutables, car plusieurs threads pourraient essayer de changer l'état du même objet, ce qui conduit certains threads à voir un état différent du même objet, en fonction de la synchronisation des lectures et des écritures sur ledit objet. objet.

En ayant un objet immuable, on peut s'assurer que tous les threads qui regardent l'objet verront le même état, car l'état d'un objet immuable ne changera pas.

coobird
la source
5
La sécurité des threads (importante pour la concurrence) n'est pas le seul avantage de l'immuabilité; cela signifie également que vous n'avez pas à faire de copies défensives des objets, et cela évite les bugs car vous ne pouvez pas modifier par erreur des objets qui ne sont pas censés être modifiés.
Jesper
7
Au lieu de renvoyer une copie de la liste, vous pouvez également return Collections.unmodifiableList(list);renvoyer une vue en lecture seule sur la liste.
Jesper
18
La classe doit être faite finalaussi. Sinon, il peut être étendu avec une méthode setter (ou d'autres types de méthodes de mutation et de champs mutables).
Abhinav Sarkar
6
@AbhinavSarkar Cela n'est pas nécessaire finalsi la classe ne contient que des privatechamps car ils ne sont pas accessibles depuis les sous-classes.
icza
3
La classe @icza doit être définitive, car nous avons la méthode publique getter, nous pouvons étendre la classe et surcharger la méthode getter puis changer le champ à notre manière ... donc il n'est plus immuable
sunil
19

En plus des réponses déjà données, je recommanderais de lire sur l'immuabilité dans Effective Java, 2e éd., Car certains détails sont faciles à manquer (par exemple, des copies défensives). De plus, Effective Java 2nd Ed. est une lecture incontournable pour chaque développeur Java.

Fabian Steeg
la source
3
C'est la ressource exacte à examiner. Les avantages mentionnés vont du code moins sujet aux erreurs à la sécurité des threads.
gpampara
6

Vous rendez une classe immuable comme ceci:

public final class Immutable
{
    private final String name;

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

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

    // No setter;
}

Voici les conditions requises pour rendre une classe Java immuable:

  • La classe doit être déclarée comme final(pour que les classes enfants ne puissent pas être créées)
  • Les membres de la classe doivent être déclarés comme final(pour que nous ne puissions pas en changer la valeur après la création de l'objet)
  • Ecrire des méthodes Getter pour toutes les variables qu'il contient pour obtenir les valeurs des membres
  • Pas de méthodes Setters

Les classes immuables sont utiles car
- Elles sont thread-safe.
- Ils expriment également quelque chose de profond à propos de votre conception: "Je ne peux pas changer cela."

duffymo
la source
5

L'immuabilité peut être obtenue principalement de deux manières:

  • en utilisant final attributs d'instance pour éviter la réaffectation
  • en utilisant une interface de classe qui n'autorise tout simplement aucune opération capable de modifier ce qui se trouve à l'intérieur de votre classe (juste des getters et pas de setters

Les avantages de l'immuabilité sont les hypothèses que vous pouvez faire sur ces objets:

  • vous gagnez la règle sans effet secondaire (qui est très populaire dans les langages de programmation fonctionnels) et vous permet d'utiliser plus facilement des objets dans un environnement concurrent, car vous savez qu'ils ne peuvent pas être modifiés de manière atomique ou non atomique lorsqu'ils le sont utilisé par de nombreux threads
  • les implémentations de langages peuvent traiter ces objets d'une manière différente, en les plaçant dans des zones de mémoire utilisées pour les données statiques, permettant une utilisation plus rapide et plus sûre de ces objets (c'est ce qui se passe dans la JVM pour les chaînes)
Jack
la source
Immuable n'est pas la même chose que sans effet secondaire égal. Par exemple, un objet immuable peut produire des effets secondaires tels que la journalisation dans un fichier. Il est légèrement inexact de dire que rendre un objet immuable le rend également sans effet secondaire.
Grundlefleck
1
@Grundleflek, je pense que c'est peut-être en train de couper les cheveux. La classe n'est pas immuable si elle modifie un fichier journal dans le cadre de son contrat et que ce fichier journal est accessible aux autres classes. Si le fichier journal est caché aux autres classes, et ne fait pas partie du contrat de la classe, alors la classe est effectivement immuable et vraiment à toutes fins utiles sans effets secondaires. L'introduction (non fournie) sur la page des effets secondaires de Wikipédia se lit comme suit: "... une expression est dite avoir un effet secondaire si, en plus de produire une valeur, elle modifie également un état ou a une interaction observable avec les fonctions d'appel."
Jeff Axelrod
3

Les classes immuables ne peuvent pas réaffecter les valeurs après leur instanciation. Le constructeur affecte des valeurs à ses variables privées. Jusqu'à ce que l'objet devienne nul, les valeurs ne peuvent pas être modifiées en raison de l'indisponibilité des méthodes de définition.

être immuable doit satisfaire la suite,

  • Toutes les variables doivent être privées .
  • Aucune méthode de mutation (setters) n'est fournie.
  • Évitez le remplacement de méthode en rendant la classe finale (Strong Immutability) ou les méthodes finales (Week immutability).
  • Clonez profondément s'il contient des classes non primitives ou mutables.

/**
* 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;
}
}

Avantages: les objets immuables contiennent leurs valeurs initialisées jusqu'à leur mort.

java-immutable-classes-short-note

Kandy
la source
2

Les classes immuables sont celles dont les objets ne peuvent pas être modifiés après la création.

Les classes immuables sont utiles pour

  • Objectif de la mise en cache
  • Environnement simultané (ThreadSafe)
  • Difficile pour l'héritage
  • La valeur ne peut être modifiée dans aucun environnement

Exemple

Classe de chaîne

Exemple de code

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;
    }
}
Yasir Shabbir Choudhary
la source
2

Comment rendre une classe Java immuable?

À partir du JDK 14+ qui a JEP 359 , nous pouvons utiliser " records". C'est le moyen le plus simple et le plus gratuit de créer une classe Immuable.

Une classe d'enregistrement est un support transparent peu profond pour un ensemble fixe de champs connu sous le nom d'enregistrementcomponents qui fournit une statedescription de l'enregistrement. Chacun componentdonne lieu à un finalchamp qui contient la valeur fournie et une accessorméthode pour récupérer la valeur. Le nom du champ et le nom de l'accesseur correspondent au nom du composant.

Prenons l'exemple de la création d'un rectangle immuable

record Rectangle(double length, double width) {}

Pas besoin de déclarer un constructeur, pas besoin d'implémenter les méthodes equals & hashCode. Tous les enregistrements ont besoin d'un nom et d'une description d'état.

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

Si vous souhaitez valider la valeur lors de la création de l'objet, nous devons déclarer explicitement le constructeur.

public Rectangle {

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

Le corps de l'enregistrement peut déclarer des méthodes statiques, des champs statiques, des initialiseurs statiques, des constructeurs, des méthodes d'instance et des types imbriqués.

Méthodes d'instance

record Rectangle(double length, double width) {

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

champs statiques, méthodes

Étant donné que l'état doit faire partie des composants, nous ne pouvons pas ajouter de champs d'instance aux enregistrements. Mais, nous pouvons ajouter des champs et des méthodes statiques:

record Rectangle(double length, double width) {

  static double aStaticField;

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

quel est le besoin d'immuabilité et y a-t-il un avantage à l'utiliser?

Les réponses précédemment publiées sont suffisamment bonnes pour justifier le besoin d'immuabilité et ce sont des avantages

Bhanu Hoysala
la source
0

Une autre façon de rendre un objet immuable consiste à utiliser la bibliothèque Immutables.org :

En supposant que les dépendances requises ont été ajoutées, créez une classe abstraite avec des méthodes d'accès abstraites. Vous pouvez faire de même en annotant avec des interfaces ou même des annotations (@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();
}

Il est maintenant possible de générer puis d'utiliser l'implémentation immuable générée:

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)
  }
}
Tout comme
la source
0

Une classe immuable est simplement une classe dont les instances ne peuvent pas être modifiées.

Toutes les informations contenues dans chaque instance sont fixes pour la durée de vie de l'objet, de sorte qu'aucun changement ne peut jamais être observé.

Les classes immuables sont plus faciles à concevoir, à implémenter et à utiliser que les classes mutables.

Pour rendre une classe immuable, suivez ces cinq règles:

  1. Ne pas fournir de méthodes qui modifient l'état de l'objet

  2. Assurez-vous que la classe ne peut pas être étendue.

  3. Rendez tous les champs définitifs.

  4. Rendre tous les champs privés.

  5. Garantissez un accès exclusif à tous les composants mutables.

Les objets immuables sont intrinsèquement thread-safe; ils ne nécessitent aucune synchronisation.

Les objets immuables peuvent être partagés librement.

Les objets immuables constituent d'excellents blocs de construction pour d'autres objets

sandeep vanama
la source
0

@Jack, Avoir les champs finaux et les setters en classe ne rendra pas la classe immuable. Le mot clé final garantit uniquement qu'une variable n'est jamais réaffectée. Vous devez renvoyer une copie complète de tous les champs dans les méthodes getter. Cela garantira qu'après avoir obtenu un objet de la méthode getter, l'état interne de l'objet n'est pas perturbé.

Amit Toor
la source
-1

En tant que non-anglophone, je n'aime pas l'interprétation courante de «classe immuable» étant «les objets de classe construits sont immuables»; plutôt, je me penche moi-même pour interpréter cela comme «l'objet de classe lui-même est immuable».

Cela dit, la "classe immuable" est une sorte d'objet immuable. La différence réside dans la réponse à l'avantage. À ma connaissance / interprétation, la classe immuable empêche ses objets de modifier le comportement à l'exécution.

bbgw
la source
-1

La plupart des réponses ici sont bonnes, et certaines ont mentionné les règles, mais je pense qu'il est bon de mettre par écrit pourquoi et quand nous devons suivre ces règles. Alors je donne l'explication ci-dessous

  • Déclarez les variables membres comme «finales» - Lorsque nous les déclarons comme finales, le compilateur nous oblige à les initialiser. Nous pouvons initialiser directement, par le constructeur par défaut, par le constructeur arg (voir l'exemple de code ci-dessous) et après l'initialisation, nous ne pouvons pas les modifier car ils sont définitifs.
  • Et bien sûr, si nous essayons d'utiliser Setters pour ces variables finales, le compilateur renvoie une erreur.

    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;
        }
    }
    

Avons-nous besoin de déclarer la classe comme «finale»?

  • Sans mot clé final dans la déclaration de classe, la classe peut être héritée. Ainsi, la sous-classe peut remplacer les méthodes getter. Ici, nous devons considérer deux scénarios:

1. N'ayant que des membres primitifs: nous n'avons pas de problème Si la classe n'a que des membres primitifs, alors nous n'avons pas besoin de déclarer la classe comme finale.

2. Avoir des objets comme variables membres: Si nous avons des objets comme variables membres, nous devons rendre les membres de ces objets également finaux. Cela signifie que nous devons traverser profondément dans l'arbre et rendre tous les objets / primitives comme finaux, ce qui peut ne pas être possible tout le temps. La solution de contournement consiste donc à rendre la classe définitive, ce qui empêche l'héritage. Il n'est donc pas question de sous-classe remplacer les méthodes getter.

Srikant M
la source
-1

L'annotation @Value de Lombok peut être utilisée pour générer des classes immuables. C'est aussi simple que le code ci-dessous.

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

Selon la documentation sur le site de Lombok:

@Value est la variante immuable de @Data; tous les champs sont rendus privés et définitifs par défaut et les setters ne sont pas générés. La classe elle-même est également rendue définitive par défaut, car l'immuabilité n'est pas quelque chose qui peut être forcé sur une sous-classe. Comme @Data, les méthodes utiles toString (), equals () et hashCode () sont également générées, chaque champ obtient une méthode getter et un constructeur qui couvre chaque argument (à l'exception des champs finaux qui sont initialisés dans la déclaration de champ) est également généré .

Un exemple pleinement fonctionnel peut être trouvé ici.

Anubhav
la source
Avant de répondre à une ancienne question ayant une réponse acceptée (recherchez le vert ✓) ainsi que d'autres réponses, assurez-vous que votre réponse ajoute quelque chose de nouveau ou est autrement utile par rapport à elles. Veuillez faire attention lorsque vous répondez à la question d'OP Comment peut-on rendre une classe Java immuable, quel est le besoin d'immuabilité et y a-t-il un avantage à l'utiliser? . - Vous ne donnez qu'une réponse partielle qui indique simplement que l'on peut utiliser Lombok, un framework / bibliothèque tiers, ce qui n'est pas nécessairement le sujet de la question d'OP. Java 15 est également sorti, pourquoi utiliser Lombok alors que Java recordpeut être utilisé?
Ivo Mori le
Je donne l'une des options pour y parvenir. Tout le monde n'utilise pas Java 15, la majorité des applications fonctionnent aujourd'hui sur des versions précédentes, vous dites donc pourquoi utiliser Lombok n'a pas beaucoup de sens. Dans mon projet actuel, nous avons récemment migré vers Java 11 et utilisons Lombok pour obtenir des classes immuables. De plus, ma réponse est un ajout aux réponses déjà présentes. Ce qui est immuable a déjà reçu une réponse, je suppose que vous vous attendez à ce que je réécrive cela juste pour terminer.
Anubhav
C'est suffisant. Je n'ai voté ni à la baisse ni à la hausse. J'ai essayé de montrer les raisons pour lesquelles deux autres personnes ont voté contre cette réponse. C'est à vous de décider si et comment vous souhaitez modifier votre réponse.
Ivo Mori le