web-dev-qa-db-fra.com

Comment créer des objets immuables en Java?

Comment créer des objets immuables en Java?

Quels objets devraient être appelés immuables?

Si j'ai cours avec tous les membres statiques, est-ce immuable?

80
Neel Salpe

Vous trouverez ci-dessous les exigences strictes d'un objet immuable.

  1. Rendre la classe finale
  2. rendre tous les membres final, les définir explicitement, dans un bloc statique ou dans le constructeur
  3. Rendre tous les membres privés
  4. Aucune méthode qui modifie l'état
  5. Soyez extrêmement prudent en limitant l'accès aux membres mutables (rappelez-vous que le champ peut être final mais que l'objet peut toujours être mutable. C'est-à-dire private final Date imStillMutable). Vous devriez faire defensive copies dans ces cas.

Le raisonnement derrière la classe final est très subtil et souvent négligé. Si ses utilisateurs finaux ne peuvent pas librement étendre votre classe, remplacez le comportement public ou protected, ajoutez des propriétés mutables, puis fournissez leur sous-classe en tant que substitut. En déclarant la classe final, vous pouvez vous assurer que cela ne se produira pas.

Pour voir le problème en action, considérons l'exemple ci-dessous:

public class MyApp{

    /**
     * @param args
     */
    public static void main(String[] args){

        System.out.println("Hello World!");

        OhNoMutable mutable = new OhNoMutable(1, 2);
        ImSoImmutable immutable = mutable;

        /*
         * Ahhhh Prints out 3 just like I always wanted
         * and I can rely on this super immutable class 
         * never changing. So its thread safe and perfect
         */
        System.out.println(immutable.add());

        /* Some sneak programmer changes a mutable field on the subclass */
        mutable.field3=4;

        /*
         * Ahhh let me just print my immutable 
         * reference again because I can trust it 
         * so much.
         * 
         */
        System.out.println(immutable.add());

        /* Why is this buggy piece of crap printing 7 and not 3
           It couldn't have changed its IMMUTABLE!!!! 
         */
    }

}

/* This class adheres to all the principles of 
*  good immutable classes. All the members are private final
*  the add() method doesn't modify any state. This class is 
*  just a thing of beauty. Its only missing one thing
*  I didn't declare the class final. Let the chaos ensue
*/ 
public class ImSoImmutable{
    private final int field1;
    private final int field2;

    public ImSoImmutable(int field1, int field2){
        this.field1 = field1;
        this.field2 = field2;
    }

    public int add(){
        return field1+field2;
    }
}

/*
This class is the problem. The problem is the 
overridden method add(). Because it uses a mutable 
member it means that I can't  guarantee that all instances
of ImSoImmutable are actually immutable.
*/ 
public class OhNoMutable extends ImSoImmutable{   

    public int field3 = 0;

    public OhNoMutable(int field1, int field2){
        super(field1, field2);          
    }

    public int add(){
       return super.add()+field3;  
    }

}

En pratique, il est très courant de rencontrer le problème ci-dessus dans les environnements d'injection de dépendance. Vous n'instanciez pas explicitement les choses et la référence à la super classe qui vous est donnée peut en fait être une sous-classe.

La conclusion est que pour faire des garanties sur l'immutabilité, vous devez marquer la classe comme final. Ceci est traité en profondeur dans Java effectif de Joshua Bloch et est explicitement référencé dans la spécification du modèle modèle de mémoire Java .

82
nsfyn55

Il suffit simplement d’ajouter des méthodes de mutateur public (setter) à la classe.

14
BalusC

Les classes ne sont pas immuables, les objets le sont.

Immuable signifie: mon état visible public ne peut pas changer après l'initialisation.

Les champs ne doivent pas nécessairement être déclarés finaux, bien que cela puisse aider énormément à assurer la sécurité du fil

Si votre classe n'a que des membres statiques, les objets de cette classe sont immuables, car vous ne pouvez pas modifier l'état de cet objet (vous ne pouvez probablement pas le créer non plus :))

13

Pour rendre une classe immuable dans Java, vous pouvez noter les points suivants:

1. Ne fournissez pas de méthodes de définition pour modifier les valeurs des variables d'instance de la classe.

2. Déclarer la classe comme 'final' . Cela empêcherait toute autre classe de l'étendre et donc de remplacer toute méthode susceptible de modifier les valeurs de variable d'instance.

3. Déclarer les variables d'instance comme privé et final.

4. Vous pouvez également déclarer le constructeur de la classe en tant que privé et ajoutez une méthode de fabrique pour créer une instance de la classe lorsque nécessaire.

Ces points devraient aider !!

6
VishEnthusiast

Depuis Oracle site, comment créer des objets immuables en Java.

  1. Ne fournissez pas de méthodes "setter" - méthodes qui modifient des champs ou des objets référencés par des champs.
  2. Rendre tous les champs finaux et privés.
  3. N'autorisez pas les sous-classes à remplacer les méthodes. Le moyen le plus simple de procéder consiste à déclarer la classe finale. Une approche plus sophistiquée consiste à rendre le constructeur privé et à construire des instances dans les méthodes d'usine.
  4. Si les champs d'instance incluent des références à des objets mutables, n'autorisez pas ces objets à être modifiés:
    JE. Ne fournissez pas de méthodes qui modifient les objets mutables.
    II. Ne partagez pas les références aux objets mutables. Ne stockez jamais de références à des objets externes, mutables, transmis au constructeur; si nécessaire, créez des copies et stockez les références aux copies. De même, créez des copies de vos objets mutables internes lorsque cela est nécessaire pour éviter de renvoyer les originaux dans vos méthodes.
3
e11438

Minimiser la mutabilité

Une classe immuable est simplement une classe dont les instances ne peuvent pas être modifiées. Toutes les informations contenues dans chaque instance sont fournies lors de sa création et sont fixes pour la durée de vie de l'objet.

Classes immuables JDK: String, les classes primitives encadrées (classes wrapper), BigInteger et BigDecimal, etc.

Comment rendre une classe immuable?

  1. Ne fournissez aucune méthode qui modifie l’état de l’objet (appelé mutateurs).
  2. Assurez-vous que la classe ne peut pas être étendue.
  3. Rendre tous les champs finaux.
  4. Rendre tous les champs privés. Cela empêche les clients d'obtenir l'accès aux objets mutables référencés par les champs et de modifier ces objets directement.
  5. Faites des copies défensives. Assure un accès exclusif à tous les composants mutables.

    public List getList () {return Collections.unmodifiableList (list); <=== copie défensive du champ mutable avant de le renvoyer à l'appelant}

Si votre classe contient des champs faisant référence à des objets mutables, assurez-vous que les clients de la classe ne peuvent pas obtenir de références à ces objets. N'initialisez jamais un tel champ sur une référence d'objet fournie par le client ou ne renvoyez pas la référence d'objet à partir d'un accesseur.

import Java.util.Date;
public final class ImmutableClass {

       public ImmutableClass(int id, String name, Date doj) {
              this.id = id;
              this.name = name;
              this.doj = doj;
       }

       private final int id;
       private final String name;
       private final Date doj;

       public int getId() {
              return id;
       }
       public String getName() {
              return name;
       }

     /**
      * Date class is mutable so we need a little care here.
      * We should not return the reference of original instance variable.
      * Instead a new Date object, with content copied to it, should be returned.
      * */
       public Date getDoj() {
              return new Date(doj.getTime()); // For mutable fields
       }
}
import Java.util.Date;
public class TestImmutable {
       public static void main(String[] args) {
              String name = "raj";
              int id = 1;
              Date doj = new Date();

              ImmutableClass class1 = new ImmutableClass(id, name, doj);
              ImmutableClass class2 = new ImmutableClass(id, name, doj);
      // every time will get a new reference for same object. Modification in              reference will not affect the immutability because it is temporary reference.
              Date date = class1.getDoj();
              date.setTime(date.getTime()+122435);
              System.out.println(class1.getDoj()==class2.getDoj());
       }
}

Pour plus d'informations, voir mon blog:
http://javaexplorer03.blogspot.in/2015/07/minimize-mutability.html

2
Rajesh Dixit

Tout d'abord, vous savez pourquoi vous devez créer un objet immuable et quels sont les avantages d'un objet immuable.

Avantages d'un objet immuable

Accès simultané et multithreading Il est automatiquement thread-safe alors problème de synchronisation .... etc

Pas besoin de copie le constructeur Pas besoin d'implémentation de clone. La classe ne peut pas être override Rendre le champ en tant que privé et final Forcer les appelants à construire un objet complètement en une seule étape, au lieu d'utiliser un constructeur sans argument

Les objets immuables sont simplement des objets dont l'état signifie que les données de l'objet ne peuvent pas changer après la construction de l'objet immuable.

s'il vous plaît voir le code ci-dessous.

public final class ImmutableReminder{
    private final Date remindingDate;

    public ImmutableReminder (Date remindingDate) {
        if(remindingDate.getTime() < System.currentTimeMillis()){
            throw new IllegalArgumentException("Can not set reminder" +
                    " for past time: " + remindingDate);
        }
        this.remindingDate = new Date(remindingDate.getTime());
    }

    public Date getRemindingDate() {
        return (Date) remindingDate.clone();
    }
}
2
J.B.Vala
  • Ne fournissez pas de méthodes "setter" - méthodes qui modifient des champs ou des objets référencés par des champs.
  • Rendre tous les champs finaux et privés.
  • N'autorisez pas les sous-classes à remplacer les méthodes. Le moyen le plus simple de procéder consiste à déclarer la classe finale. Une approche plus sophistiquée consiste à rendre le constructeur privé et à construire des instances dans les méthodes d'usine.
  • Si les champs d'instance incluent des références à des objets mutables, n'autorisez pas la modification de ces objets:
    • Ne fournissez pas de méthodes qui modifient les objets mutables.
    • Ne partagez pas les références aux objets mutables. Ne stockez jamais de références à des objets externes, mutables, transmis au constructeur; si nécessaire, créez des copies et stockez les références aux copies. De même, créez des copies de vos objets mutables internes lorsque cela est nécessaire pour éviter de renvoyer les originaux dans vos méthodes.
2
Ajay Kumar

Un objet immuable est un objet qui ne changera pas son état interne après la création. Ils sont très utiles dans les applications multithreads car ils peuvent être partagés entre les threads sans synchronisation.

Pour créer un objet immuable, vous devez suivre quelques règles simples:

1. N'ajoutez aucune méthode de définition

Si vous construisez un objet immuable, son état interne ne changera jamais. La tâche d'une méthode de définition consiste à modifier la valeur interne d'un champ, de sorte que vous ne pouvez pas l'ajouter.

2. Déclarer tous les champs final et privé

Un champ privé n'est pas visible de l'extérieur de la classe. Aucune modification manuelle ne peut donc y être appliquée.

Déclarer un champ final garantira que, s'il référence une valeur primitive, la valeur ne changera jamais s'il référence un objet, la référence ne peut pas être modifiée. Cela ne suffit pas pour garantir qu'un objet comportant uniquement des champs finaux privés ne soit pas mutable.

. Si un champ est un objet mutable, créez-en des copies défensives pour les méthodes getter

Nous avons vu précédemment que définir un champ final et private n'est pas suffisant car il est possible de changer son état interne. Pour résoudre ce problème, nous devons créer une copie défensive de ce champ et le renvoyer chaque fois qu'il est demandé.

4. Si un objet mutable passé au constructeur doit être affecté à un champ, créer une copie défensive de celui-ci

Le même problème se produit si vous détenez une référence transmise au constructeur car il est possible de la modifier. Ainsi, le fait de conserver une référence à un objet transmis au constructeur peut créer des objets mutables. Pour résoudre ce problème, il est nécessaire de créer une copie défensive du paramètre s'il s'agit d'objets mutables.

Notez que si un champ est une référence à un objet immuable, il n'est pas nécessaire de créer des copies défensives de celui-ci dans le constructeur et dans les méthodes de lecture, il suffit de définir le champ comme final et privé.

5. Ne pas autoriser les sous-classes à remplacer les méthodes

Si une sous-classe remplace une méthode, elle peut renvoyer la valeur d'origine d'un champ mutable au lieu d'une copie défensive de celle-ci.

Pour résoudre ce problème, il est possible de procéder de l’une des manières suivantes:

  1. Déclarez la classe immuable en tant que finale afin qu'elle ne puisse pas être étendue
  2. Déclarer toutes les méthodes de la classe immuable final afin qu'elles ne puissent pas être annulées
  3. Créer un constructeur privé et une fabrique pour créer des instances de la classe immuable, car une classe avec des constructeurs privés ne peut pas être étendue

Si vous suivez ces règles simples, vous pouvez librement partager vos objets immuables entre les threads car ils sont thread-safe!

Voici quelques points notables:

  • Les objets immuables rendent effectivement la vie plus simple dans de nombreux cas. Ils sont particulièrement applicables aux types de valeur, où les objets n’ont pas d’identité, ils peuvent donc être facilement remplacés et ils peuvent rendre la programmation simultanée plus sûre et plus propre état mutable partagé entre les threads). Cependant, pour les objets volumineux et/ou complexes, créer une nouvelle copie de l'objet à chaque changement peut être très coûteux et/ou fastidieux. Et pour les objets ayant une identité distincte, modifier un objet existant est beaucoup plus simple et intuitif que de créer une nouvelle copie modifiée de celui-ci.
  • Certaines choses que vous ne pouvez tout simplement pas faire avec des objets immuables, telles que relations bidirectionnelles. Une fois que vous avez défini une valeur d'association sur un objet, son identité change. Donc, vous définissez la nouvelle valeur sur l'autre objet et cela change également. Le problème est que la référence du premier objet n'est plus valide, car une nouvelle instance a été créée pour représenter l'objet avec la référence. Si cela continuait, les régressions seraient infinies.
  • Pour implémenter un arbre de recherche binaire, vous devez retourner un nouvel arbre à chaque fois: votre nouvel arbre devra faire une copie de chaque nœud modifié (les branches non modifiées sont partagées). . Ce n'est pas si grave pour votre fonction d'insertion, mais pour moi, les choses sont devenues assez inefficaces rapidement lorsque j'ai commencé à travailler sur la suppression et le rééquilibrage.
  • Hibernate et JPA déterminez essentiellement que votre système utilise des objets mutables, car ils reposent sur le principe fondamental qu'ils détectent et enregistrent les modifications apportées à vos objets de données.
  • Selon le langage utilisé, un compilateur peut procéder à de nombreuses optimisations lorsqu'il s'agit de données immuables, car il sait que les données ne changeront jamais. Toutes sortes de choses sont ignorées, ce qui vous donne d’énormes avantages en termes de performances.
  • Si vous examinez d'autres langages JVM connus (Scala, Clojure), les objets mutables sont rarement vus dans le code. C'est pourquoi les gens commencent à les utiliser dans des scénarios où un seul thread ne suffit pas.

Il n'y a pas de bien ou de mal, cela dépend de ce que vous préférez. Cela dépend de vos préférences et de ce que vous voulez réaliser (et être en mesure d'utiliser facilement les deux approches sans aliéner les fans inconditionnels d'un côté ou de l'autre est un saint graal que certaines langues recherchent).

2
amitkumar12788

Les quelques étapes suivantes doivent être prises en compte lorsque vous souhaitez qu'une classe soit immuable.

  1. La classe devrait être marquée comme finale
  2. Tous les champs doivent être privés et définitifs
  3. Remplacez les setters par constructor (pour assigner une valeur à une variable).

Jetons un coup d'oeil ce que nous avons tapé ci-dessus:

//ImmutableClass
package younus.attari;

public final class ImmutableExample {

    private final String name;
    private final String address;

    public ImmutableExample(String name,String address){
        this.name=name;
        this.address=address;
    }


    public String getName() {
        return name;
    }

    public String getAddress() {
        return address;
    }

}

//MainClass from where an ImmutableClass will be called
package younus.attari;

public class MainClass {

    public static void main(String[] args) {
        ImmutableExample example=new ImmutableExample("Muhammed", "Hyderabad");
        System.out.println(example.getName());

    }
}
1
user3205589

un objet est appelé immuable si son état ne peut pas être changé une fois créé. L’un des moyens les plus simples de créer une classe immuable dans Java) consiste à définir tous ses champs comme étant définitifs. Si vous devez écrire une classe immuable comprenant des classes mutables telles que "Java.util.Date" Afin de préserver l’immuabilité dans de tels cas, il est conseillé de renvoyer une copie de l’objet original,

1
Rudra

Les objets immuables sont les objets dont l'état ne peut plus être modifié une fois qu'ils ont été créés. Par exemple, la classe String est une classe immuable. Les objets immuables ne peuvent pas être modifiés, ils sont donc également sécurisés pour les threads lors d'une exécution simultanée.

Caractéristiques des classes immuables:

  • simple à construire
  • automatiquement thread-safe
  • bon candidat pour les touches de la carte et réglé car leur état interne ne changerait pas lors du traitement
  • pas besoin d'implémentation de clone car ils représentent toujours le même état

Clés pour écrire une classe immuable:

  • assurez-vous que la classe ne peut pas être remplacée
  • rendre tous les membres variables privés et finaux
  • ne pas donner leurs méthodes de setter
  • la référence de l'objet ne doit pas être divulguée pendant la phase de construction
1
Manjul

Propriétés généralement ignorées mais importantes sur les objets immuables

En plus de la réponse fournie par @ nsfyn55, les aspects suivants doivent également être pris en compte pour l’immuabilité des objets, qui sont d’importance première

Considérez les classes suivantes:

public final class ImmutableClass {

  private final MutableClass mc;

  public ImmutableClass(MutableClass mc) {
    this.mc = mc;
  }

  public MutableClass getMutClass() {
    return this.mc;
  }
}

public class MutableClass {

  private String name;

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

  public void setName(String name) {
    this.name = name;
  }
}


public class MutabilityCheck {

public static void main(String[] args) {

  MutableClass mc = new MutableClass();

  mc.setName("Foo");

  ImmutableClass iMC = new ImmutableClass(mc);

  System.out.println(iMC.getMutClass().getName());

  mc.setName("Bar");

  System.out.println(iMC.getMutClass().getName());

  }

 }

Voici le résultat de MutabilityCheck:

 Foo
 Bar

Il est important de noter que,

  1. Construction d'objets mutables sur un objet immuable (via le constructeur), soit en 'copie' ou 'cloing' en variables d'instance de l'immuable décrites par les modifications suivantes:

    public final class ImmutableClass {
    
       private final MutableClass mc;
    
       public ImmutableClass(MutableClass mc) {
         this.mc = new MutableClass(mc);
       }
    
       public MutableClass getMutClass() {
         return this.mc;
       }
    
     }
    
     public class MutableClass {
    
      private String name;
    
      public MutableClass() {
    
      }
      //copy constructor
      public MutableClass(MutableClass mc) {
        this.name = mc.getName();
      }
    
      public String getName() {
        return this.name;
      }
    
      public void setName(String name) {
       this.name = name;
      } 
     }
    

n'assure toujours pas une immuabilité complète car ce qui suit est toujours valable à partir de la classe MutabilityCheck:

  iMC.getMutClass().setName("Blaa");
  1. Cependant, si vous exécutez MutabilityCheck avec les modifications apportées dans 1., le résultat sera:

    Foo
    Foo
    
  2. Pour obtenir une immuabilité complète sur un objet, tous ses les objets dépendants doivent également être immuables

0
KJ Sudarshan