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

Langage Java Discussion :

getter, setter et références sur objets


Sujet :

Langage Java

  1. #1
    Membre expérimenté Avatar de rtg57
    Homme Profil pro
    Autodidacte
    Inscrit en
    Mars 2006
    Messages
    1 340
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 57
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Autodidacte
    Secteur : Service public

    Informations forums :
    Inscription : Mars 2006
    Messages : 1 340
    Points : 1 576
    Points
    1 576
    Par défaut getter, setter et références sur objets
    Bonjour,

    lorsque j'ai débuté en JAVA, j'avais lu un article concernant la protection des références sur les objets, je crois que cela parlait d'encapsulation.
    Sauf que à l'époque, je n'y comprenais pas grand-chose et j'avais d'autres soucis avec JAVA.

    Actuellement, je développe une application, dont une classe à des objets et variables membres privés.
    Je permets aux autres classes de l'application d'accéder aux valeurs de ces membres, via une interface équipée de getters et setters. Du genre:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    public void setNomPrenom( String sNomPrenom )
    {
      this.nomPrenom = sNomPrenom;
    }
     
    public String getNomPrenom()
    {
      return this.nomPrenom;
    }
    Comme je commence à comprendre certaines choses en JAVA (enfin c'est ce que je crois ), j'ai l'impression que ce code n'est pas très sécurisé.
    En effet, j'ai lu qu'en JAVA, on transmet des références sur les objets.

    1) Dans setNomPrenom( String sNomPrenom ), je transmets une référence sur un objet String, et je copie cette référence dans l'objet membre nomPrenom.
    Je pense que dans la classe appelante, celle-là même qui a fourni sNomPrenom, je modifie le contenu de cette chaine de caractères, alors je modifie aussi le contenu de nomPrenom ? Vrai ou Faux ??

    2) Dans getNomPrenom(), je transmets une référence sur l'objet membre ( qui est défini comme private), à la classe qui en fait la demande. Celle-ci ayant cette référence, a donc tout loisir de modifier le contenu d'un élément auquel elle ne devrait pourtant pas avoir accès. Vrai ou Faux ??

    Je ne retrouve plus l'article traitant de ce sujet, mais je crois qu'aujourd'hui j'en aurais bien besoin

    Avez-vous des tuyaux à me fournir sur le sujet ?

    Merci & @ bientôt...
    @ bientôt...

    Salut & @+ sur 3W!

  2. #2
    Membre éprouvé
    Profil pro
    Inscrit en
    Avril 2007
    Messages
    764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2007
    Messages : 764
    Points : 909
    Points
    909
    Par défaut
    Alors, déjà, ton exemple est mal choisi...
    En effet, les String sont immutables : les méthodes de la classe String ne modifient jamais l'instance à laquelle elles sont appliquées (les méthodes replace ou substring, par exemple, renvoient une nouvelle instance de String).

    Par contre, sur le principe, tu as raison.
    Si tu donnes directement accès à un objet, tu pourras y appliquer toutes les méthodes de cet objet en question, et donc éventuellement modifier l'état de l'instance concernée.

    Exemple:
    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
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    public class Trucmuche
    {
    	private List<String> list;
     
    	public Trucmuche() {
    		this.list = new ArrayList<String>();
    	}
     
    	public void setList(List<String> list) {
    		this.list = list;
    	}
     
    	public List<String> getList() {
    		return this.list;
    	}
     
    	public void print() {
    		System.out.println("Liste à " + this.list.size() + " éléments:");
    		Iterator<String> it = this.list.iterator();
    		while (it.hasNext()) {
    			System.out.println(it.next());
    		}
    		System.out.println("----------");
    	}
     
    	public static void main(String[] args)
    	{
    		Trucmuche chose = new Trucmuche();
    		chose.print();
     
    		chose.getList().add("bli"); // j'ajoute un élément à la liste...
    		chose.getList().add("bla"); // j'ajoute un élément à la liste...
    		chose.getList().add("blu"); // j'ajoute un élément à la liste...
    		chose.print();
    		chose.getList().remove(1); // j'enlève un élément de la liste...
    		chose.print();
     
    		List<String> newList = new ArrayList<String>(Arrays.asList(new String[] {"hihi"}));
    		chose.setList(newList); // je remplace la liste...
    		chose.print();
    		newList.add("huhu"); // j'ajoute un élément à la liste sans même faire référence à chose...
    		chose.print();
    	}
    }
    En donnant directement accès à l'objet list, je permets de faire n'importe quoi avec cet objet.

    La solution pour limiter les actions possibles : l'encapsulation
    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
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    public class Trucmuche
    {
    	private List<String> list;
     
    	public Trucmuche() {
    		this.list = new ArrayList<String>();
    	}
     
    	// la seule action externe autorisée sur la liste : l'ajout d'éléments
    	public void addToList(String item) {
    		this.list.add(item);
    	}
     
    	public void print() {
    		System.out.println("Liste à " + this.list.size() + " éléments:");
    		Iterator<String> it = this.list.iterator();
    		while (it.hasNext()) {
    			System.out.println(it.next());
    		}
    		System.out.println("----------");
    	}
     
    	public static void main(String[] args)
    	{
    		Trucmuche chose = new Trucmuche();
    		chose.print();
     
    		chose.addToList("bli"); // j'ajoute un élément à la liste...
    		chose.addToList("bla"); // j'ajoute un élément à la liste...
    		chose.addToList("blu"); // j'ajoute un élément à la liste...
    		chose.print();
    	}
    }
    Ici, j'ai encapsulé la fonction "ajout d'un élément à la liste" dans la méthode addToList, et c'est la seule action que je présente à l'extérieur de ma classe. Je contrôle les actions que j'autorise l'extérieur à effectuer sur mon objet list.

  3. #3
    Membre expérimenté Avatar de rtg57
    Homme Profil pro
    Autodidacte
    Inscrit en
    Mars 2006
    Messages
    1 340
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 57
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Autodidacte
    Secteur : Service public

    Informations forums :
    Inscription : Mars 2006
    Messages : 1 340
    Points : 1 576
    Points
    1 576
    Par défaut
    Bonjour,

    merci déjà pour la précision au sujet de la classe String.

    Si j'ai bien compris, lorsqu'une classe demande la valeur d'un objet à travers un getter(), il sera judicieux de fournir une instance provisoire "copie" de l'objet:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    public Date getDate()
    {
      return new Date( this.maDateMembre );
    ]
    Est-ce que ce raisonnement est juste ?

    Quant à un setter, il faudrait faire un truc du genre:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    public void setDate( Date laDatetransmise )
    {
      this.maDateMembre = new ( laDateTransmise );
    }
    Est-ce que ça c'est juste aussi ?

    @ bientôt...
    @ bientôt...

    Salut & @+ sur 3W!

  4. #4
    Membre habitué

    Profil pro
    Inscrit en
    Mai 2010
    Messages
    36
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2010
    Messages : 36
    Points : 163
    Points
    163
    Par défaut
    Bonjour rtg57,
    [copie dans getter]
    [copie dans setter]

    Est-ce que ça c'est juste aussi ?
    Tu as parfaitement raison: c'est ce qu'on appelle la copie défensive. Accessoirement, tu peux aussi faire tes copies avec clone() particulièrement quand tu ne connais pas la classe concrète des objets que tu manipules.

    Bien sûr, dans les deux cas (new ou clone), cela implique un surcoût lié à la multiplication d'objets copiés. L'alternative est d'utiliser des objets immutables (comme les string).

    http://gfx.developpez.com/tutoriel/java/immuables/

    - Sylvain

  5. #5
    Modérateur

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

    Informations forums :
    Inscription : Septembre 2004
    Messages : 12 551
    Points : 21 607
    Points
    21 607
    Par défaut
    À noter que bien que tout cela soit vrai en théorie, et doive bel et bien être appliqué dans certains cas quand on se rend compte qu'il y a risque...

    Dans le monde réel et la plupart du temps, ben, on s'en cogne. Quand un bout de programme n'est pas censé modifier un objet, on peut généralement s'attendre à ce que le programmeur le sache. C'est pas le genre d'erreur qui va arriver spontanément. Et si, par hasard, les choses, l'interface, la manière de faire ; tout ça font que, en fait, l'erreur ait des chances d'arriver ; alors dans ce cas, oui, il faut savoir faire de la copie défensive.
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  6. #6
    Membre habitué

    Profil pro
    Inscrit en
    Mai 2010
    Messages
    36
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2010
    Messages : 36
    Points : 163
    Points
    163
    Par défaut
    Comme souvent en informatique, il n'y a pas de "bonne" ou de "mauvaise" réponse. Tout dépend du contexte.

    Si tu écris une application, tu peux partir du principe que les objets de cette application ne requièreront sans doute pas de copie défensive: après tout, tu peux dire que c'est toi ou ton équipe qui codez les classes correspondantes et qui les utilisez. Et en résumé, vous savez ce que vous faites.

    A l'inverse, si tu écris une bibliothèque - à fortiori une que tu vas distribuer à l'extérieur - j'aurais tout de même tendance à plaider pour la copie défensive. Peut-être pas pour tes classes internes, mais au moins au niveau de tes objets "interfaces" ou de tes "facades". Le premier argument est d'éviter les bugs liés à une mauvaise utilisation. D'accord, comme le souligne thelvin, il faut parfois le chercher. Mais justement, si tu distribues une bibliothèque et que des développeurs se rendent compte qu'avec ce genre de "hack" ils peuvent modifier l'état interne de tes objets pour contourner une limitation ou adapter le comportement de ta bibliothèque à leurs besoins, ils ne vont pas forcément s'en priver
    Et là, implicitement tu rompt l'encapsulation. A partir de ce moment, tu as un problème de couplage fort avec le code client: en effet, si tu modifies ton code, (genre "dans la version 2, on utilise des Calendar au lieu de Date en interne") tu risques de "casser" le code client qui repose sur le fonctionnement interne de tes classe. Selon ta politique, ça peut être le problème du client ("z'ont pas à utiliser des comportements non documentés") ou au contraire ton problème et celui de ton management ("depuis que tu as fais ce changement, l'appli critique de notre plus gros client plante. Va falloir me corriger ça mon gars...").

    ----

    De façon plus terre à terre. Pour palier (partiellement?) à la réduction (minime?) de performances liées à l'utilisation de la copie défensive, la JVM HotSpot server de Java 7 devrait pouvoir éliminer certaines allocations inutiles (http://java.sun.com/javase/7/docs/te...escapeAnalysis). Ca n'est sans doute pas la panacée, mais ça pourrait limiter les scrupules liés à l'utilisation plus systématique de la copie défensive.

    ----

    Maintenant, je répète ce que j'ai dit en préambule: c'est selon ton contexte. L'important, je crois, c'est juste d'être conscient des implications de ce que tu fais.

    A+,
    - Sylvain

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

    Citation Envoyé par thelvin Voir le message
    Dans le monde réel et la plupart du temps, ben, on s'en cogne.
    Heu... pas vraiment !

    En Java la plupart des classes de bases sont immuables (String, les différents Numbers...) et la copie est implicite sur les type primitif... Mais le problème reste présent si on utilise un type muable comme Date.

    Ce n'est pas vraiment un problème de mauvaise utilisation volontaire, mais plutôt de mauvaise utilisation involontaire...

    D'ailleurs le fait que la classe Date ne soit pas immuable est une erreur reconnu. C'est un peu pour cela que la plupart de ses méthodes ont été déprécié et que Java 7 devrait inclure une nouvelle API avec des types immuable.


    Par exemple avec ce code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    Date date = myObject.getDate(); // On récupère la date de l'objet
    date.setTime( date.getTime() + xxx  );
    // puis on utilise date pour autre chose
    1. On récupère la date de l'objet
    2. On la modifie (par exemple on ajoute un jour)
    3. On utilise la date modifié pour un traitement quelconque, indépendant de notre objet

    Ce code semble des plus anodin, mais on a quand même modifier la date de notre objet, en outrepassant le getter...


    Bref : il faut privilégier les types immuables, ou penser à bien protéger ses getter/setter via une copie défensive...

    a++

  8. #8
    Modérateur

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

    Informations forums :
    Inscription : Septembre 2004
    Messages : 12 551
    Points : 21 607
    Points
    21 607
    Par défaut
    Date est un cas qui me semble un peu particulier, justement parce qu'il devrait être immutable.

    Mais en général, il n'y a pas de programmation défensive de ce genre dans la bibliothèque de base Java ni dans la plupart des bibliothèques très connues. Sauf dans les cas où on flaire un risque réel d'erreur. Ça ne pose pas de problème, et vu que c'est plus léger à lire, c'est pratiquement un style de programmation extrêmement repris.
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  9. #9
    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
    Citation Envoyé par thelvin Voir le message
    Mais en général, il n'y a pas de programmation défensive de ce genre dans la bibliothèque de base Java ni dans la plupart des bibliothèques très connues.
    Parce qu'en général on utilise des types immuables avec lesquels la copie défensive est inutile...

    Sinon la copie défensive est bien présente, y compris dans l'API de base (par exemple avec les classes Dimension ou Point dans l'API Swing).

    Dans tous les cas, une absence de copie défensive sur un attribut non-immuable est source d'erreur potentiel. Je ne vois pas en quoi il pourrait y avoir un risque d'erreur moins important dans certains cas...

    a++

  10. #10
    Membre habitué

    Profil pro
    Inscrit en
    Mai 2010
    Messages
    36
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2010
    Messages : 36
    Points : 163
    Points
    163
    Par défaut
    Comme ce sujet est matière à une intéressante divergence d'opinions, je me suis replongé dans le très bon Effective Java (second edition) (ISBN 0321356683) pour vérifier la position de l'auteur sur ce sujet.


    Citation Envoyé par p184, Item 39: Make defensive copies when needed
    You must program defensively with the assumption that clients of your class will do their best to destroy its invariants.
    Le texte de la première édition est disponible ici: http://www.informit.com/articles/art...31551&seqNum=2

    L'auteur argumente sur la création de copies défensives dans les getter/setter (ainsi que pour les paramètres des constructeurs). Par ailleurs, pour une modification, il suggère de faire la copie avant de vérifier la validité des paramètres:

    Code java : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    public void setSomeDate(Date date) {
        /* 1) copier */
        Date theDate = new Date(date.getTime());
        /* 2) vérifier la validité de la copie */
        if (isValid(theDate)) {
            /* 3) remplacer la propriété par la copie */
            myDate = theDate;
        }
    }

    La raison d'agir ainsi est d'éviter la fenêtre de vulnérabilité entre la validation de la valeur et la copie, pendant laquelle un autre thread pourrait modifier la donnée.

    Enfin, dans le même esprit - et contrairement à ce que je suggérais plus haut - l'auteur déconseille aussi d'utiliser clone() pour faire une copie défensive d'une donnée quand celle-ci n'est pas issue d'une source de confiance: en effet, il fait remarquer à bon escient que le paramètre passé peut être une instance d'une classe dérivée spécialement conçue pour se comporter de manière malicieuse.


    Tout ces arguments me semblent convainquant. Et vous, "Dans le monde réel", vous faites comme ça?


    - Sylvain

  11. #11
    Membre expérimenté Avatar de rtg57
    Homme Profil pro
    Autodidacte
    Inscrit en
    Mars 2006
    Messages
    1 340
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 57
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Autodidacte
    Secteur : Service public

    Informations forums :
    Inscription : Mars 2006
    Messages : 1 340
    Points : 1 576
    Points
    1 576
    Par défaut
    Bonjour a tous,

    je suis content que ma question soulève débat ( j'avais peur que ce soit une question niveau "ras des pâquerette" ).
    En tous cas, ce que vous avez écrit m'intéresse fortement.
    J'ai fait des essais du genre
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    Date maDate1 = ...;
    Date maDate2 = ...;
    puisafin de voir quand est-ce qu'il y a copie de référence, ou copie de valeur.
    J'ai essayé avec String et d'autres objets...la question qui me vient maintenant: comment sait-on que l'on travaille avec un objet "immuable" ?

    Je ne vois rien "d'évident" lorsque je lis la documentation SUN.
    Avez-vous une combine là-dessus ?

    Merci & @ bientôt...
    @ bientôt...

    Salut & @+ sur 3W!

  12. #12
    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
    Citation Envoyé par rtg57 Voir le message
    afin de voir quand est-ce qu'il y a copie de référence, ou copie de valeur.
    C'est simple : lorsque tu manipules des objets tu auras toujours une copie de la référence, quoi qu'il arrive.
    La copie par valeur ne s'applique qu'aux types primitifs.

    Citation Envoyé par rtg57 Voir le message
    comment sait-on que l'on travaille avec un objet "immuable" ?

    Je ne vois rien "d'évident" lorsque je lis la documentation SUN.
    Avez-vous une combine là-dessus ?
    Malheureusement il n'y a rien de clair et précis.
    Le plus simple étant de rechercher le terme "immutable" dans la doc de la classe, pour voir si ils en parlent (mais ce n'est pas forcément le cas )

    Sinon les classes immuables se distinguent par l'absence de setter() et par le mot-clef final qui interdit toute classe fille... mais ce n'est pas les seules conditions !!!


    a++

  13. #13
    Membre éclairé Avatar de Heimdal
    Profil pro
    Inscrit en
    Avril 2006
    Messages
    549
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2006
    Messages : 549
    Points : 718
    Points
    718
    Par défaut
    Salut,

    j'appuie tout à fait la position d'Adiguba et de sleroux et dans le monde réel je privilégie et conseille fortement l'utilisation d'objets immuables (pour pas dire que je ne fais que ça), n'expose et ne code que le strict nécessaire, fais de la copie défensive lorsque nécessaire et renvoie systématiquement des collections non-modifiables (Collections.unmodifiable*(* *)).

    Sinon concernant la question :
    comment sait-on que l'on travaille avec un objet "immuable" ?
    C'est écrit dans la javadoc. Sinon tu peux regarder si ton objet contient des mutateurs ou non... Dans la majorité des cas ces derniers seront des void (à l'exception des Builders qui se renvoie eux-même)

  14. #14
    Membre habitué

    Profil pro
    Inscrit en
    Mai 2010
    Messages
    36
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2010
    Messages : 36
    Points : 163
    Points
    163
    Par défaut
    Citation Envoyé par rtg57
    J'ai fait des essais [...] afin de voir quand est-ce qu'il y a copie de référence, ou copie de valeur.
    Peut-être que tu viens d'un background C++ où le code suivant effectue une copie de l'objet (éventuellement via son copy constructor). Mais en Java ça n'est jamais le cas:
    Code c++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    Date maDate1 = maDate2;
    // en C++, copie de l'objet: maDate1 et maDate2 sont deux objets différents
    // en Java copie de la référence sur l'objet: maDate1 et maDate2 désignent le même objet
    Comme l'a dit adiGuba, hormi pour les types primitifs (int, float, etc. <- ceux qui commencent par une minuscule), il n'y a jamais de copie implicite. Tu dois explicitement faire la copie toi-même (new, clone(), en passant par une factory, ...). Le fait qu'un objet soit mutable ou immutable n'altère en rien ce comportement.

    Où ça joue, c'est qu'un objet immutable, tu sais que son état ne peut pas changer. Tu peux donc sans scrupule renvoyer une référence sur une propriété immutable, puisque tu es certain que le code client ne pourra pas en altérer le contenu.
    De la même manière, si tu reçois en paramètre un objet immutable, tu sais que tu peux l'utiliser sans faire de copie, car son contenu ne pourra pas changer pendant que tu t'en sert.

    Citation Envoyé par adiGuba
    Sinon les classes immuables se distinguent par l'absence de setter() et par le mot-clef final
    Euh, le fait qu'une classe soit finale, ne la rend pas immutable:
    Code java : 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
    21
    22
    23
    24
    25
    26
    27
    28
    29
     
    //
    // Classe marquée "final" mais l'objet est "mutable" quand même
    //
    final class Mutable {
        private int n;
     
        public void setN(int n) {
            this.n = n;
        }
     
        public int getN() {
            return n;
        }
    }
     
    public class C {
        public static void main(String args[]) {
            //
            // La variable est marquée "final" mais ça n'empèche pas de modier
            // l'état de l'objet "mutable"
            //
            final Mutable mutable = new Mutable();
            mutable.setN(5);
            System.out.println(mutable.getN());
            mutable.setN(10);
            System.out.println(mutable.getN());
        }
    }
    M'est avis que je n'ai pas compris...


    - Sylvain

  15. #15
    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
    Citation Envoyé par adiGuba Voir le message
    Euh, le fait qu'une classe soit finale, ne la rend pas immutable
    Non bien sûr... J'ai indiqué que ce n'était pas les seules conditions

    Le problème c'est que si rien n'est indiqué dans la doc, on ne peut pas garantir qu'une classe est immuable sans analyser son code...


    a++

  16. #16
    Membre habitué

    Profil pro
    Inscrit en
    Mai 2010
    Messages
    36
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2010
    Messages : 36
    Points : 163
    Points
    163
    Par défaut
    Le problème c'est que si rien n'est indiqué dans la doc, on ne peut pas garantir qu'une classe est immuable sans analyser son code...
    C'est vrai que c'est particulièrement pénible. J'ai toujours trouvé qu'il manquait à Java un modificateur "const" comme en C++...


    Sinon, pour en revenir au modificateur final, j'y ai réfléchi depuis tout à l'heure. L'avantage d'avoir une classe "final", c'est qu'on est certain que l'instance qui est reçue en argument est bien une instance de la classe immutable - et pas d'une classe spécialement conçue pour avoir un comportement malicieux. Je serais peut-être un poil plus clair sur un exemple.

    Considérons la classe suivante - dont les instances sont immutables:
    Code java : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    class Immutable {
        Immutable(int n) {
            this.n = n;
        }
     
        public int getN() {
            return n;
        }
     
        final private int n;
    }
    Et le code d'une classe cliente - tout aussi immutable en apparence:
    Code java : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    class Client {
        public Client(Immutable immutable) {
            this.immutable = immutable;
        }
     
        public int getImmutableValue() {
            return immutable.getN();
        }
     
        final Immutable immutable;
    }

    On peut utiliser l'ensemble ainsi:
    Code java : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
            final Immutable immutable = new Immutable(5);
            final Client    client    = new Client(immutable);
     
            // Affiche 3 fois la même valeur "évidemment":
            System.out.println(client.getImmutableValue());
            System.out.println(client.getImmutableValue());
            System.out.println(client.getImmutableValue());

    Le problème c'est que Client n'est pas aussi immutable que ça! En effet, on peut l'abuser en forgeant une classe dérivé de Immutable ainsi:
    Code java : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    class Fool extends Immutable {
        public Fool(int n) {
            super(n);
        }
     
        public int getN() {
                return random.nextInt();
        }
     
        private Random random = new Random();
    }
    Du coup, on peut casser le fonctionnement de Client juste en passant une instance de Fool au constructeur:
    Code java : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
            final Immutable immutable = new Fool(5);
            final Client    client    = new Client(immutable);
     
            // Affiche 3 valeurs différentes:
            System.out.println(client.getImmutableValue());
            System.out.println(client.getImmutableValue());
            System.out.println(client.getImmutableValue());

    Effectivement, ce qu'il manque ici, c'est d'ajouter un modificateur "final" à la classe Immutable pour empêcher de fabriquer la classe dérivée Fool. D'un autre côté, on peut se demander si ici on est toujours dans le cas d'une erreur d'utilisation involontaire - ou d'un comportement franchement hostile?
    Mais, qu'elles soit volontaire ou non, personnellement j'aurais quand même tendance à croire que ça ne fait que du bien de se prémunir contre ce genre d'erreur...

    Comme quoi, c'est une question qui est loin d'être au "ras des pâquerette"

    - Sylvain

  17. #17
    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
    Citation Envoyé par sleroux Voir le message
    C'est vrai que c'est particulièrement pénible. J'ai toujours trouvé qu'il manquait à Java un modificateur "const" comme en C++...
    const a quand même un rôle assez différent... dû justement à l'absence de type immuable.



    A un moment donné ils y avaient des discussions sur l'ajout d'une annotation @Immutable pour marquer ces classes, le tout couplé avec des vérifications supplémentaires du compilateur... mais ca date et il n'y a rien de neuf de ce coté là !


    Citation Envoyé par sleroux Voir le message
    Sinon, pour en revenir au modificateur final, j'y ai réfléchi depuis tout à l'heure. L'avantage d'avoir une classe "final", c'est qu'on est certain que l'instance qui est reçue en argument est bien une instance de la classe immutable - et pas d'une classe spécialement conçue pour avoir un comportement malicieux.
    Oui le final permet d'être sûr du type exact de la classe, et donc de son immuabilité. En règle général une classe non-final ne doit pas être considéré comme immuable, justement car une classe fille pourrait "casser" l'immuabilité...

    Citation Envoyé par sleroux Voir le message
    D'un autre côté, on peut se demander si ici on est toujours dans le cas d'une erreur d'utilisation involontaire - ou d'un comportement franchement hostile?
    Cela peut être malicieux bien sûr... mais cela pourrait également correspondre à un code tout à fait correct et même sans erreur je dirais ! Après tout rien n'interdit de modifier le comportement de la classe parente...



    a++

  18. #18
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 481
    Points : 48 806
    Points
    48 806
    Par défaut
    Je vais donner un coup de pieds dans la fourmillière de la copie défensive.

    -> ça coute du temps CPU
    -> ça coute de la mémoire (on a la copie et l'argument)
    -> Il n'est pas toujours possible de faire une copie (les classes complexes n'implémentant pas cloneable ne sont pas légion)
    -> Dans la plupart des cas, vous AVEZ besoin que les objets dans votre classe vivent, la copie n'est donc pas souhaitable.

    Et les classe java qui référencent d'autres objets sans copie sont légion (Toute l'api collection, toute l'api swing), et certaines exigent pourtant une invariance. Je vais prendre le hashset, si le hash de l'objet change dans le temps, ça fout en l'air le SET, mais le hashset ne fait pas pourtant un clone de l'objet à stocker. En fait, la copie, en générale, n'est pas souhaitable car on veux récupérer exactement la même référence que celle que l'ont a envoyée. Si j'ai une classe "Agenda", j'aimerais quand même que les objet personnes que je met dedans ne soient pas immutable. Et en général (l'exception de l'objet date a déjà été mentionné), les objet java complexes sont destiné à être mutable, donc on en fait pas la copie.

    Il faut toujours se poser la question "si l'objet que je stocke se fait tripatouiller, ça a quelles conséquences pour moi?"

    Pour moi, j'utilise très peu la copie défensive, on ne s'est que rarement posé la question dans l'équipe, les seules fois où la question se posaient, c'est justement parce que la modification aurait posé problème. Bref, si tu ne ressent pas le besoin de te poser la question, c'est que le problème n'existe pas. Quand tu aura des cas nécessitant une copie, tu t'en rendra compte Mais c'est rare (bien que j'en conviens, ca dépend du logiciel développé)

  19. #19
    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
    En l'absence des types immuables, tu aurais bien plus de recourt à la copie défensive

    Après c'est sûr qu'il y a deux cas à différencier :
    • Les attributs que tu as besoin de contrôler précisément (exemple dans l'API Swing : la position et la taille).
    • Les attributs que tu te contente de référencer pour y accéder plus facilement, sans forcément avoir à t'inquiéter de leurs modifications (exemple dans Swing : les composants fils).



    Quand aux Set/Map, il est indiqué dans la doc qu'il faut utiliser des types immuables comme clef

    a++

  20. #20
    Membre expérimenté Avatar de rtg57
    Homme Profil pro
    Autodidacte
    Inscrit en
    Mars 2006
    Messages
    1 340
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 57
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Autodidacte
    Secteur : Service public

    Informations forums :
    Inscription : Mars 2006
    Messages : 1 340
    Points : 1 576
    Points
    1 576
    Par défaut
    Je vous remercie tous pour votre aide.

    L'origine de cette question vient du fait que j'ai tendance à utiliser une variable ou un objet, plusieurs fois dans une même méthode. Je m'explique:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    void maMethode()
    {
      int variable1;
     
      for( variable1 = ...bla bla )
     
    /.../
     
      variable1 = uneReponseDeFonction();
     
    /.../
    }
    Avec une variable primitive, y a pas de soucis. On peut imaginer maintenant le cas avec un objet:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    void maMethode()
    {
      Date maDate;
     
      maDate = maClasse.getDateNaissance(); // Le fameux getter
     
    /.../
     
      maDate = uneReponseDeFonction();
     
    /.../
    }
    * Si maClasse.getDateNaissance() fourni la référence du membre maClasse.dateNaissance...
    * Et si uneReponseDeFonction() modifie le contenu de maDate (l'exemple est peut être mal choisi)...
    alors je modifie "par mégarde" le contenu du membre maClasse.dateNaissance, qui est pourtant déclaré comme private; et voilà la bourde !!

    Je ne cherche pas à protéger le code de mes classes (car je programme pour moi ), je cherche plutôt à faire du code "blindé", à l'épreuve de mes bêtises.

    J'ai programmé quelques années en C++, et je n'ai pas encore la philosophie JAVA, c'est pour cela que je fais du code pas très catholique. Il y a peut être une astuce qui permet de se passer de la copie défensive...

    @ bientôt...
    @ bientôt...

    Salut & @+ sur 3W!

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. Réponses: 0
    Dernier message: 25/02/2014, 16h01
  2. [POO] Perte de la référence sur mon objet (this) lors d'un évènement
    Par muad'dib dans le forum Général JavaScript
    Réponses: 5
    Dernier message: 20/12/2008, 12h59
  3. référence sur la méthode d'un objet
    Par kirbby dans le forum Langage
    Réponses: 2
    Dernier message: 25/04/2008, 11h17
  4. Réponses: 2
    Dernier message: 13/12/2006, 13h39
  5. [VB6] faire référence à un objet situé sur un autre form
    Par coyott dans le forum VB 6 et antérieur
    Réponses: 1
    Dernier message: 15/05/2006, 15h13

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