IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Java Discussion :

Questions sur les classes immuables (par rapport au tutoriel)


Sujet :

Java

  1. #1
    Modérateur
    Avatar de Gugelhupf
    Homme Profil pro
    Analyste Programmeur
    Inscrit en
    Décembre 2011
    Messages
    1 320
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Analyste Programmeur

    Informations forums :
    Inscription : Décembre 2011
    Messages : 1 320
    Points : 3 741
    Points
    3 741
    Billets dans le blog
    12
    Par défaut Questions sur les classes immuables (par rapport au tutoriel)
    Bonjour,

    Je suis en train de lire le tutoriel de Romain Guy (Gfx), sur les classes et objets immuables.

    Il cite les règles d'or à appliquer pour rendre sa classe immuable :
    1. La classe doit être déclarée final (dans le cas contraire, il serait possible de modifier une instance par héritage)
    2. Tous les champs doivent être déclarés final
    3. La référence à this ne doit jamais être exportée
    4. Tous les champs faisant référence à un objet non immuable doivent être privés, ne jamais être exportés, représenter l'unique référence à cet objet et ne jamais modifier l'état de l'objet
    Pour le point n°3 : Lorsque l'auteur dit "La référence à this ne doit jamais être exportée", est-ce que cela signifie que la classe ne doit pas contenir de méthode avec return this ?
    Parce qu'une fois qu'on aura créé une instance de classe immuable (ex: String), et qu'on aura créé des objets qui pointent dessus, ce sera comme si on pointait sur this de l'objet immuable ?

    Pour le point n°4 : Lorsque l'auteur dit pour les champs "ne jamais être exportés", "ne jamais modifier l'état de l'objet" => Est-ce que cela signifie que la classe immuable ne doit pas contenir de getter et de setter ?


    Merci
    N'hésitez pas à consulter la FAQ Java, lire les cours et tutoriels Java, et à poser vos questions sur les forums d'entraide Java

    Ma page Developpez | Mon profil Linkedin | Vous souhaitez me contacter ? Contacter Gokan EKINCI

  2. #2
    Modérateur

    Profil pro
    Inscrit en
    Septembre 2004
    Messages
    12 557
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2004
    Messages : 12 557
    Points : 21 616
    Points
    21 616
    Par défaut
    Citation Envoyé par Gugelhupf Voir le message
    Pour le point n°3 : Lorsque l'auteur dit "La référence à this ne doit jamais être exportée", est-ce que cela signifie que la classe ne doit pas contenir de méthode avec return this ?
    Parce qu'une fois qu'on aura créé une instance de classe immuable (ex: String), et qu'on aura créé des objets qui pointent dessus, ce sera comme si on pointait sur this de l'objet immuable ?
    Je comprends pas trop ce point 3, moi non plus.

    Non, je pense qu'il fait plutôt référence à des appels de méthode auxquels on passerait this en paramètre, genre UneAutreClasse.traiter(this);. Il dit qu'il faut pas faire ça. Mais je ne sais pas trop pourquoi il le dit comme ça.

    Trimballer this dans un return ou en paramètre d'un appel de méthode, en général ça a l'air inutile dans des classes immutables, donc on est plus propre si on le fait pas... Sauf que la classe String est un bon contre-exemple, dans substring() et matches() par exemple. Du coup ça n'a pas l'air d'être un problème de faire ça avec this.

    Il n'y a un problème potentiel, que si on fait ça dans les constructeurs ou les blocs d'initialisation. Parce qu'à ce moment-là, l'objet n'est pas encore complètement construit et va encore muter, tant que le constructeur n'est pas fini. Il est donc encore dans un état mutable, et ne respecte donc pas sa nature d'instance de classe immutable.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    public final class A {
      public final int a;
     
      {
        UneAutreClasse.traiter(this); // SURTOUT PAS !!
        notifier(); // NON PLUS !!
      }
     
      public A() {
        UneAutreClasse.traiter(this); // SURTOUT PAS !!
        notifier(); // NON PLUS !!
        this.a = 8;
      }
     
      public void notifier() {
        // pas spécialement un problème si cette méthode n'est pas appelée
        // par un constructeur ou un bloc d'initialisation
        UneAutreClasse.traiter(this); 
      }
    }
    Mais trimballer this avant la fin du constructeur, c'est à éviter en toute circonstance. Pas seulement pour les classes immutables, mais jamais, point final.
    C'est normal puisque le principe d'un constructeur est de faire la construction complète de l'instance jusqu'à son état attendu, et donc qu'elle n'est pas dans son état attendu tant que le constructeur n'est pas fini.

    Je vois donc pas trop l'intérêt du point 3.

    Citation Envoyé par Gugelhupf Voir le message
    Pour le point n°4 : Lorsque l'auteur dit pour les champs "ne jamais être exportés", "ne jamais modifier l'état de l'objet" => Est-ce que cela signifie que la classe immuable ne doit pas contenir de getter et de setter ?
    Pas vraiment. Déjà, des setters, c'est évident qu'ils n'ont rien à faire sur une classe immutable. Un setter sert à modifier l'instance, et l'idée d'une classe immutable c'est que ses instances ne peuvent pas être modifiées -_-°. Il ne faut pas mettre de setter, c'est pas le point 4 qui le dit, c'est le bon sens.

    Mais si la classe contient des membres d'un type mutable, comme Date, il ne faut pas donner d'accès direct à cette Date. Pour les raisons données dans le tutoriel. Mais rien n'empêche de faire un getter(). C'est juste que ce getter doit faire une copie défensive de la donnée, et refiler cette copie au lieu de la donnée originale. Comme montré dans le tutoriel.
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  3. #3
    Modérateur
    Avatar de Gugelhupf
    Homme Profil pro
    Analyste Programmeur
    Inscrit en
    Décembre 2011
    Messages
    1 320
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Analyste Programmeur

    Informations forums :
    Inscription : Décembre 2011
    Messages : 1 320
    Points : 3 741
    Points
    3 741
    Billets dans le blog
    12
    Par défaut
    Bonjour,

    Merci pour votre réponse.

    Donc si je comprend bien pour le point n°3 : il ne faut pas que le this soit utilisé par un objet externe dans le constructeur d'une classe immuable...
    Mais quels sont les risques exactement ? Que le this soit initialisé par un objet externe et que plusieurs objets externes risquent de pointer vers les attributs de la classe immuable ?
    Donc si this n'est pas initialisé par un objet externe, je respecte la règle suivante : Tous les champs faisant référence à un objet [...] ne jamais être exportés ?



    Mais il y a un autre problème dans ce tutoriel, l'auteur dit :
    Ils (classes immuables) n'ont besoin ni de constructeur par copie, , ni d'implémentation de l'interface Cloneable.

    Pourtant la classe String (qui est immuable), et possède un constructeur de copie. Comment faire pour copier (ou cloner), une classe immuable sans constructeur de copie ?

    Merci
    N'hésitez pas à consulter la FAQ Java, lire les cours et tutoriels Java, et à poser vos questions sur les forums d'entraide Java

    Ma page Developpez | Mon profil Linkedin | Vous souhaitez me contacter ? Contacter Gokan EKINCI

  4. #4
    Modérateur

    Profil pro
    Inscrit en
    Septembre 2004
    Messages
    12 557
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2004
    Messages : 12 557
    Points : 21 616
    Points
    21 616
    Par défaut
    Citation Envoyé par Gugelhupf Voir le message
    Donc si je comprend bien pour le point n°3 : il ne faut pas que le this soit utilisé par un objet externe dans le constructeur d'une classe immuable...
    Ni d'une classe mutable d'ailleurs. Du coup je ne vois pas pourquoi ce tutoriel en parle.

    Citation Envoyé par Gugelhupf Voir le message
    Mais quels sont les risques exactement ?
    Entre le début du constructeur et sa fin, l'instance va changer d'état. Quiconque aurait accès à this à ce moment-là, verrait donc un objet qui va muter, donc mutable, et non pas immutable. Il ne pourrait pas compter sur les propriétés immutables de cet objet. Ce qui enlève tout l'intérêt de la chose, censée être absolue et certaine.

    Citation Envoyé par Gugelhupf Voir le message
    Que le this soit initialisé par un objet externe
    On ne peut pas "initialiser" this. Et un objet externe ne peut pas modifier l'instance pointée par this, puisque tous ses champs sont final et qu'il n'y a aucune méthode permettant de modifier l'instance.
    L'objet externe ne peut pas modifier cette instance. Mais cette instance risque de se modifier elle-même, puisque son constructeur n'est pas terminé.

    Citation Envoyé par Gugelhupf Voir le message
    et que plusieurs objets externes risquent de pointer vers les attributs de la classe immuable ?
    Je ne vois pas en quoi ce serait un problème.

    Citation Envoyé par Gugelhupf Voir le message
    Pourtant la classe String (qui est immuable), et possède un constructeur de copie.
    C'est vrai que dans le cas de String ça pouvait servir. Parce que quand on faisait substring(), la nouvelle String construite ne construisait pas son propre char[] interne, elle utilisait le même char[] que la String dont elle est issue, et indiquait juste où elle commençait et se terminait.
    De ce fait, si on a une String de 500000 caractères, et qu'on fait substring() pour garder que les 2 premiers, on se trimballe en mémoire le char[] de 500000 caractères. Le constructeur par copie permettait de recréer un nouveau char[] indépendant, qui du coup aurait pile la bonne taille au lieu de garder 499998 caractères inutiles.

    Depuis Java 7 ce n'est plus le cas. substring() crée un nouveau char[] de pile la bonne taille, comme le constructeur par copie. Le constructeur par copie ne sert donc plus à rien.

    Citation Envoyé par Gugelhupf Voir le message
    Comment faire pour copier (ou cloner), une classe immuable sans constructeur de copie ?
    Tu peux pas. Ça servirait à quoi ?
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  5. #5
    Membre chevronné
    Inscrit en
    Mai 2006
    Messages
    1 364
    Détails du profil
    Informations forums :
    Inscription : Mai 2006
    Messages : 1 364
    Points : 1 984
    Points
    1 984
    Par défaut
    Citation Envoyé par Gugelhupf Voir le message
    Donc si je comprend bien pour le point n°3 : il ne faut pas que le this soit utilisé par un objet externe dans le constructeur d'une classe immuable...
    Mais quels sont les risques exactement ? Que le this soit initialisé par un objet externe et que plusieurs objets externes risquent de pointer vers les attributs de la classe immuable ?
    Que l'objet soit immuable ou non ne change rien par rapport à ca. Comme dit par thelvin, il ne faut pas passer une reference à this à un autre objet dans son constructeur tout simplement parce que l'objet n'est pas initialisé à ce moment la (puisque par hypothèse, on est dans le constructeur) et donc que ses methodes/attributs peuvent ne pas etre dans un état stable.

    Citation Envoyé par Gugelhupf Voir le message
    Donc si this n'est pas initialisé par un objet externe, je respecte la règle suivante : Tous les champs faisant référence à un objet [...] ne jamais être exportés ?
    Comme on l'a dit, il ne faut pas initialiser un objet dans un autre (en tout cas, c'est risqué). Mais ca n'a pas grand chose à voir avec le point 3. La regle, c'est que si est un objet est muable (donc qu'il peut changer), il ne faut pas exporter une reference sur celui-ci puisqu'étant muable, il pourrait etre modifié par un appelant. C'est pourquoi il faut faire une copie defensive.

    Citation Envoyé par Gugelhupf Voir le message
    Mais il y a un autre problème dans ce tutoriel, l'auteur dit :
    Ils (classes immuables) n'ont besoin ni de constructeur par copie, , ni d'implémentation de l'interface Cloneable.

    Pourtant la classe String (qui est immuable), et possède un constructeur de copie. Comment faire pour copier (ou cloner), une classe immuable sans constructeur de copie ?
    C'est la tout l'interet des classes immuables. Comme elles ne peuvent pas changer, il n'est pas utile de les cloner. Lorsqu'on a besoin d'un objet pour en initialiser un autre, il est frequent de garder une copie plutot que la reference à l'original (dans le tuto, ca correspond à l'exemple de Date) pour s'assurer qu'il ne change pas apres l'appel du constructeur (par exemple, immagine une classe à laquelle tu passes une Date qui est sensée générer une Exception si Date < 01/01/2010. Si tu gardes une référence à l'objet Date, celle-ci peut etre changée pour une date qui ne correspond plus apres validation de celle-ci par le constructeur). Dans le cas d'un objet immuable, il suffit de garder une reference de l'original et de l'utiliser lorsqu'il y en a besoin puisqu'elle est par définition constante. Elle n'a donc pas besoin de constructeur par recopie ou bien de clone. Ceci dit, ca ne veut pas dire qu'il est interdit d'en avoir.

    Dans le cas que tu présentes de String, ca sert au moins à une chose. Il faut savoir qu'un objet String est composé d'un char[] qui représente le texte, d'un int qui représente l'offset et d'un int qui représente la longueur. Lorsque tu fais appel à String.substring, une référence au char[] original est gardée (c'est juste l'offset et la longueur qui changent). Du coup, si tu extrais une chaine tres courte d'une autre tres longue, ca consomme de la mémoire pour rien. Il convient donc de faire une copie (grace à ce constructeur) pour mieux utiliser la mémoire.

  6. #6
    Modérateur
    Avatar de Gugelhupf
    Homme Profil pro
    Analyste Programmeur
    Inscrit en
    Décembre 2011
    Messages
    1 320
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Analyste Programmeur

    Informations forums :
    Inscription : Décembre 2011
    Messages : 1 320
    Points : 3 741
    Points
    3 741
    Billets dans le blog
    12
    Par défaut
    Citation: Envoyé par Gugelhupf
    Comment faire pour copier (ou cloner), une classe immuable sans constructeur de copie ?
    Tu peux pas. Ça servirait à quoi ?
    J'ai pensé qu'aucun attribut de la classe immuable devait être pointé par un objet externe. Mais ce n'est pas grave pour les instances de classe immuable.


    J'aimerais poser quelques questions (puis j'ouvrirais peut-être un nouveau thread pour Cloneable ) :
    • Peut-t-on parler de "Design Pattern Immutable" ? (C'est assez vague sur internet).
    • Comment la JVM peut être sur que ma classe est Immuable comme String pour le mettre en cache ? Existe-t-il des outils de test pour savoir si la classe est Immuable ?



    Merci
    N'hésitez pas à consulter la FAQ Java, lire les cours et tutoriels Java, et à poser vos questions sur les forums d'entraide Java

    Ma page Developpez | Mon profil Linkedin | Vous souhaitez me contacter ? Contacter Gokan EKINCI

  7. #7
    Membre chevronné
    Inscrit en
    Mai 2006
    Messages
    1 364
    Détails du profil
    Informations forums :
    Inscription : Mai 2006
    Messages : 1 364
    Points : 1 984
    Points
    1 984
    Par défaut
    Citation Envoyé par Gugelhupf Voir le message
    Peut-t-on parler de "Design Pattern Immutable" ? (C'est assez vague sur internet).
    Deja, il faut savoir ce qu'est un design pattern. En fait, c'est simplement un recueil de bonnes pratiques liées à une fonction particulière. L'idée c'est que quand on a un problème donné, on regarde dans les design patterns s'il n'y a pas un truc qui ressemble et on l'applique si on trouve. Ca permet de ré-utiliser des techniques de gens qui se sont creusé la tete pendant des heures plutot que de réinventer la roue. Mais bon, on est sur un sujet philosophique, pas technique.

    Citation Envoyé par Gugelhupf Voir le message
    Comment la JVM peut être sur que ma classe est Immuable comme String pour le mettre en cache ?
    La JVM ne sait pas que c'est une classe immuable. Ce qui va changer, c'est ta maniere d'utiliser la classe, et c'est ca qui fera la différence. Par exemple en ne synchronisant pas les acces à tes objects, si c'est un code tres utilisé, ca peut apporter des gains significatifs. De la meme maniere, le fait de savoir que ta classe est immuable evite que tu aies a garder des copies partout. La consommation mémoire est donc meilleure (ce qui peut etre pas mal, en particulier sur des machines embarquées qui n'ont pas autant de mémoire qu'un PC).
    Mais pour en revenir au cache, il faut savoir que meme celui de la classe Integer est géré dans celle-ci et non au niveau de la JVM...

  8. #8
    Expert éminent sénior
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Points : 23 190
    Points
    23 190
    Billets dans le blog
    1
    Par défaut
    Salut,


    Je rajouterais quelques petites nuances dans les deux premières affirmations :
    1. La classe doit être déclarée final (dans le cas contraire, il serait possible de modifier une instance par héritage)
    2. Tous les champs doivent être déclarés final

    1. Il n'est pas obligatoire que la classe soit final.
    On peut très bien se contenter d'un constructeur private qui limite l'héritage au classe interne.
    Cela peut permettre d'utiliser plusieurs implémentation, via des classes internes et une méthode de fabrique...



    2. Il n'est pas obligatoire que tous les champs soient déclarés final.
    Il "suffit" juste de s'assurer que l'état de l'objet ne change pas pendant toute sa durée de vie...
    Bien sûr utiliser final est le moyen le plus simple de s'en assurer, mais il est possible d'utiliser des champs non-final que l'on calculera seulement en cas de besoin (c'est le cas par exemple du hashCode de la classe String).




    a++

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Questions sur les classes.
    Par DarkSeiryu dans le forum Windows Forms
    Réponses: 8
    Dernier message: 06/02/2009, 09h21
  2. Questions sur les matrices (commençons par les termes)
    Par beegees dans le forum Algorithmes et structures de données
    Réponses: 5
    Dernier message: 29/01/2009, 00h26
  3. Un question sur les classes
    Par willycat dans le forum C++
    Réponses: 10
    Dernier message: 22/08/2008, 17h55
  4. Questions sur les classes en C#
    Par greg2 dans le forum C#
    Réponses: 11
    Dernier message: 05/11/2007, 19h57
  5. Question sur les variables passées par URL
    Par cotlod dans le forum Langage
    Réponses: 7
    Dernier message: 11/10/2006, 00h04

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo