web-dev-qa-db-fra.com

Exemples de classes immuables

Je connais déjà la définition des classes immuables mais il me faut quelques exemples. 

44
user635041

Quelques classes immuables célèbres dans l'API standard:

  • Java.lang.String (déjà mentionné)
  • Les classes wrapper des types primitifs suivants: Java.lang.Integer, Java.lang.Byte, Java.lang.Character, Java.lang.Short, Java.lang.Boolean, Java.lang.Long, Java.lang.Double, Java.lang.Float
  • Java.lang.StackTraceElement (utilisé dans la construction de stacktraces d'exceptions)
  • La plupart des classes d'énumération sont immuables, mais cela dépend en fait du cas concret. (N'implémentez pas d'énums mutables, cela vous bousillera un peu.) Je pense qu'au moins toutes les classes d'énum de l'API standard sont en fait immuables.

  • Java.math.BigInteger et Java.math.BigDecimal (au moins les objets de ces classes elles-mêmes, les sous-classes pourraient introduire une mutabilité, bien que ce ne soit pas une bonne idée)

  • Java.io.File. Notez que cela représente un objet externe à la VM (un fichier sur le système local), qui peut exister ou non, et certaines méthodes permettent de modifier et d'interroger l'état de cet objet externe. Mais l'objet File lui-même reste immuable. (Toutes les autres classes de Java.io sont mutables.)

  • Java.awt.Font - représentant une police pour dessiner du texte à l'écran (il peut y avoir des sous-classes mutables, mais cela ne serait certainement pas utile)

  • Java.awt.BasicStroke - un objet utilitaire permettant de tracer des lignes dans des contextes graphiques
  • Java.awt.Color - (au moins les objets de cette classe, certaines sous-classes peuvent être mutables ou dépendre de facteurs externes (comme les couleurs système)) et de la plupart des autres implémentations de Java.awt.Paint telles que
    • Java.awt.GradientPaint,
    • Java.awt.LinearGradientPaint
    • Java.awt.RadialGradientPaint, 
    • (Je ne suis pas sûr de Java.awt.TexturePaint)
  • Java.awt.Cursor - représentant le bitmap du curseur de la souris (ici aussi, certaines sous-classes peuvent être mutables ou dépendre de facteurs externes)

  • Java.util.Locale - représentant une région géographique, politique ou culturelle spécifique .

  • Java.util.UUID - autant que possible un identifiant global unique
  • bien que la plupart des collections soient modifiables, la classe Java.util.Collections contient des méthodes d'encapsulation qui renvoient une vue non modifiable sur une collection. Si vous leur transmettez une collection inconnue nulle part, il s’agit en fait de collections immuables. De plus, Collections.singletonMap(), .singletonList, .singleton renvoient des collections immuables à un élément et il en existe également des vides immuables.

  • Java.net.URL et Java.net.URI - représentant une ressource (sur Internet ou ailleurs)

  • Java.net.Inet4Address et Java.net.Inet6Address, Java.net.InetSocketAddress 
  • la plupart des sous-classes de Java.security.Permission (représentant les autorisations nécessaires à une action ou à un code), mais pas Java.security.PermissionCollection et ses sous-classes.
  • Toutes les classes de Java.time sauf DateTimeException sont immuables. La plupart des classes des sous-paquets de Java.time sont également immuables.

On pourrait dire que les types primitifs sont également immuables - vous ne pouvez pas changer la valeur de 42, n'est-ce pas?


class AccessControlContext est-il une classe immuable

AccessControlContext n'a aucune méthode de mutation. Et son état consiste en une liste de ProtectionDomains (qui est une classe immuable) et d'un DomainCombiner. DomainCombiner est une interface, de sorte que l'implémentation peut en principe faire quelque chose de différent à chaque appel.

En fait, le comportement de ProtectionDomain pourrait également dépendre de la politique en vigueur - il est discutable de dire si un tel objet doit être immuable.

et AccessController?

Il n'y a pas d'objets de type AccessController, puisqu'il s'agit d'une classe finale sans constructeur accessible. Toutes les méthodes sont statiques. On pourrait dire que AccessController n'est ni mutable, ni immuable, ni les deux.

La même chose est valable pour toutes les autres classes qui ne peuvent avoir d'objets (instances), notamment:

  • Java.lang.Void
  • Java.lang.System (mais cela a un état statique mutable - in, out, err)
  • Java.lang.Math (cela aussi - le générateur de nombres aléatoires)
  • Java.lang.reflect.Array
  • Java.util.Collections
  • Tableaux Java.util
82
Paŭlo Ebermann

Les classes immuables ne peuvent pas être modifiées après la construction. Ainsi, par exemple, une Java String est immuable. 

Pour rendre une classe immuable, vous devez la rendre final et tous les champs private et final. Par exemple, la classe suivante est immuable:

public final class Person {

     private final String name;
     private final int age;
     private final Collection<String> friends;

     public Person(String name, int age, Collection<String> friends) {
         this.name = name;
         this.age = age;
         this.friends = new ArrayList(friends);
     }

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

     public int getAge() {
         return this.age;
     }

     public Collection<String> getFriends() {
         return Collections.unmodifiableCollection(this.friends);
     }
}

J'ai ajouté une méthode dans l'exemple de code montrant comment gérer les collections, un point important.

Si possible vous devriez rendre les classes immuables, car vous n'avez pas à vous soucier de choses telles que la sécurité des threads.

22
christophmccann

Il est important de garder à l'esprit que déclarer une classe comme finale ne signifie pas qu'elle est "immuable", cela signifie essentiellement que cette classe ne peut pas être étendue (ou spécialisée).

Les classes immuables doivent avoir des champs privés et finaux (sans séparateurs). Par conséquent, après sa construction, les valeurs de champ ne peuvent pas être modifiées.

13
Luciano Costa

Pour créer une classe immuable, vous devez suivre les étapes suivantes:

  1. Déclarez la classe comme finale afin qu’elle ne puisse pas être étendue.
  2. Rendre tous les champs privés afin que l'accès direct ne soit pas autorisé.
  3. Ne fournissez pas de méthodes de définition pour les variables
  4. Définissez tous les champs mutables comme finaux afin que leur valeur puisse être affectée .__ une seule fois.
  5. Initialise tous les champs via un constructeur effectuant une copie en profondeur.
  6. Effectuez le clonage d'objets dans les méthodes getter pour renvoyer une copie Plutôt que de renvoyer la référence réelle à l'objet.

Un exemple peut être trouvé ici .

Nous pouvons également utiliser Builder Pattern pour créer facilement des classes immuables, un exemple peut être trouvé ici .

9
Pankaj

Les classes LocalDate, LocalTime _ et LocalDateTime (depuis la version 1.8) sont également immuables. En fait, ce sujet même est à l'examen OCAJSE8 (1Z0-808), et c'est précisément pourquoi j'ai décidé de le traiter comme un simple commentaire.

Toutes les classes wrapper primitives (telles que Boolean _, Caractère, Byte, Court, Integer, Long, Float _ et Double) sont immuables.

Money et Devise API (destinée à Java9) devrait également être immuable.

Incidemment, les listes sauvegardées en tableau (créées par Arrays.asList(myArray)) sont structurellement -immutables.

De plus, il existe des cas limites tels que Java.util.Optional (figurant dans l'examen OCP, 1Z0-809), qui est immuable si l'élément contenu est lui-même immuable.

7
Igor Soudakevitch

La documentation de Sun (Oracle) contient une excellente liste de contrôle sur la création d’un objet immuable. 

  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 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.

De: http://download.Oracle.com/javase/tutorial/essential/concurrency/imstrat.html

Le site fournit également des exemples de son utilisation dans un contexte de concurrence, mais l'immuabilité est également utile lors de l'écriture de bibliothèques. Cela garantit que les appelants de la bibliothèque ne peuvent changer que ce que nous leur permettons.

3
Alkaline

String est un bon exemple du "monde réel" d'une classe immuable. Et vous pouvez le comparer à la classe mutable StringBuilder.


La plupart des classes Java utilisées pour la réflexion sont immuables. Et certains des autres sont "presque immuables": par exemple. les classes qui implémentent Accessible ont juste une méthode setAccessible qui modifie l'état de l'instance Accessible.


Je suis sûr qu'il y en a beaucoup plus dans les bibliothèques de classes standard.

3
Stephen C

La classe immuable est une classe qui, une fois créée, ne peut être modifiée. Les objets immuables sont les objets dont l'état ne peut pas être changé une fois construit. Exemple - Chaîne & toutes les classes de wrapper Java.

Les objets mutables sont les objets dont l'état peut être modifié une fois construit.example- StringBuffer Une fois la valeur modifiée, l'emplacement mémoire modifié . Voir ci-dessous exemple -

 public static void immutableOperation(){
    String str=new String("String is immutable class in Java object value cann't alter once created...");
    System.out.println(str);
    str.replaceAll("String", "StringBuffer");
    System.out.println(str);
    str.concat("Concating value ");
    System.out.println(str + "HashCode Value  " + str.hashCode());
    str=str.concat("Concating value ");
    System.out.println(str + "HashCode Val  " + str.hashCode());

}

public static void mutableOperation(){
    StringBuffer str=new StringBuffer("StringBuffer is mutable class in Java object value can  alter once created...");
    System.out.println(str + "HashCode Val - " + str.hashCode());
    str.replace(0, 12, "String");
    System.out.println(str + "HashCode Val - " + str.hashCode());

}
2
jaibardhan

J'aime utiliser des exemples qui ont une propriété mutable. Cela aide à comprendre comment fonctionnent réellement les classes immuables.

Classe mutable

class MutableBook {
    private String title;

    public String getTitle(){
        return this.title;
    }

    public void setTitle(String title){
        this.title = title;
    }
}

Et une implémentation immuable utilisant l'instance mutable d'un livre.

public class ImmutableReader {
    private final MutableBook readersBook;
    private final int page;

    public ImmutableReader(MutableBook book) {
        this(book, 0);
    }

    private ImmutableReader(MutableBook book, int page){
        this.page = page;

        // Make copy to ensure this books state won't change.
        MutableBook bookCopy = new MutableBook();
        bookCopy.setTitle(book.getTitle());
        this.readersBook = bookCopy;
    }


    public MutableBook getBook() {
        // Do not return the book, but a new copy. Do not want the readers
        // book to change it's state if developer changes book after this call.
        MutableBook bookCopy = new MutableBook();
        bookCopy.setTitle(this.readersBook.getTitle());
        return bookCopy;
    }

    public int getPage() {
        // primitives are already immutable.
        return page;
    }

    /**
    * Must return reader instance since it's state has changed.
    **/ 
    public ImmutableReader turnPage() {
        return new ImmutableReader(this.readersBook, page + 1);
    }
}

Pour que votre classe soit vraiment immuable, elle doit répondre aux conditions suivantes:

  • Tous les membres de la classe sont déclarés finaux.
  • Toutes les variables utilisées dans une classe au niveau classe doivent être instanciées lors de la construction de la classe.
  • Aucune variable de classe ne peut avoir de méthode de définition .
    • Ceci est implicite à partir de la première déclaration, mais vous voulez préciser que vous ne pouvez pas changer l'état de la classe.
  • Tous les objets enfants doivent également être immuables, ou leur état n'a jamais changé dans la classe immuable .
    • Si vous avez une classe avec des propriétés mutables, vous devez la verrouiller. Déclarez-le privé et veillez à ne jamais changer d'état.

Pour en savoir un peu plus, jetez un coup d'œil à mon article de blog: http://keaplogik.blogspot.com/2015/07/Java-immutable-classes-simplified.html

1
keaplogik

Lors de la création d'un objet d'une classe immuable, il faut s'assurer que la référence externe ne sera pas stockée. Cependant, les valeurs importent ici. Dans l'exemple ci-dessous, j'ai une classe appelée Fruits dans laquelle se trouve une liste. J'ai rendu la classe immuable en rendant la liste privée et finale ainsi qu’aucun passeur n’est fourni.

  1. Si j'instancie un objet de Fruit, le constructeur se verra attribuer une liste. Le programme client possède déjà une référence de cette liste (côté client) et permet donc de modifier facilement la liste, ce qui entraîne la perte de son immuabilité.

  2. Pour résoudre ce problème, je crée une nouvelle liste dans le constructeur qui copie toutes les valeurs fournies par le client.

  3. Maintenant, si le client ajoute plus de valeurs dans la liste, la référence externe sera affectée, cependant, je ne stocke plus cette référence externe dans ma classe immuable.

  4. Cela peut être vérifié en remplaçant un hashcode () dans la classe immuable. Peu importe le nombre de fois que le client modifie la liste, le hashcode de mon objet de classe immuable restera inchangé, car la liste qu'il accepte ne pointe plus vers la liste externe.

public class Fruit {

private final List<String> fruitnames;

public Fruit(List<String> fruitnames) {
    this.fruitnames = new ArrayList<>(fruitnames);
}

public List<String> getFruitnames() {
     return new ArrayList<>(fruitnames);
}

@Override
public int hashCode() {
    return getFruitnames() != null ? getFruitnames().hashCode(): 0;
}

}

// programme client

classe publique ImmutableDemo {

public static void main(String args[]){

    List<String> fruitList = new ArrayList<>();
    fruitList.add("Apple");
    fruitList.add("Banana");
    //Immutable Object 1
    Fruit fruit1 = new Fruit(fruitList);
    //fruitHash is-689428840
    int fruitHash = fruit1.hashCode();

    System.out.println("fruitHash is" +fruitHash);
    //This value will not be added anymore as the state has already been defined and
    //now it cant change the state.
    fruitList.add("straberry");
    //fruitHash1 is-689428840
    int fruitHash1 = fruit1.hashCode();


    System.out.println("fruitHash1 is" +fruitHash1);
}

}

0
Amruta