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 :

Problème copie d'objet


Sujet :

Langage Java

  1. #1
    Membre averti
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2008
    Messages
    38
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Avril 2008
    Messages : 38
    Par défaut Problème copie d'objet
    Bonjour,

    J'ai le code suivant:

    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
        public void displayLegend() {
            // Affichage de la légende
            Metaphore leg= new Metaphore();
            leg=myTable.getTabMeta()[0];
            int nb = leg.nbForme();
     
            for (int i=0;i<nb;i++)
            {
                leg.getTabForme()[i].setLargeurForme((float)30);
                leg.getTabForme()[i].setTailleMinForme((float)30);
            }
     
     
            legend.actualise(leg.sceneForme(false, true));
        }
    Le problème est que quand je modifie les attributs de leg (dans la boucle for), cela modifie aussi la métaphore issue de myTable.getTabMeta()[0], alors que j'avais crée leg justement dans le but que la métaphore de base ne soit pas affectée par les changement.

    Quelqu'un pourrait m'aider s'il vous plaît?

    Merci

  2. #2
    Membre Expert

    Homme Profil pro
    Consultant informatique
    Inscrit en
    Janvier 2004
    Messages
    2 301
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Consultant informatique
    Secteur : Finance

    Informations forums :
    Inscription : Janvier 2004
    Messages : 2 301
    Par défaut
    Hello,

    il faut faire un clônage en profondeur.

    http://ydisanto.developpez.com/tutor...2se/cloneable/

  3. #3
    Expert confirmé Avatar de Flodelarab
    Homme Profil pro
    Inscrit en
    Septembre 2005
    Messages
    5 300
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente (Poitou Charente)

    Informations forums :
    Inscription : Septembre 2005
    Messages : 5 300
    Par défaut
    Pour que tu comprennes bien:
    leg désigne un objet de type Metaphore. Mais tu n'as qu'un pointeur vers un objet et pas l'objet lui-même.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    Métahpore leg; // leg est défini mais il n'y a pas d'objet au bout. leg=null
    leg=new Metaphore(); // leg a un nouvel objet au bout du pointeur (qu'il y ait ou non un objet avant)
    leg=myTable.getTabMeta()[0]; // leg pointe sur un objet déjà existant. tu recopies le pointeur mais pas l'objet !!!
    Quand un objet n'est plus pointé par rien, la mémoire est récupérée par le garbage collector.

    Si tu as compris ces 3 commentaires, tu comprends que dans:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
            Metaphore leg= new Metaphore();
            leg=myTable.getTabMeta()[0];
    la partie orange ne sert à rien. Tu définis un pointeur, tu crées un nouvel objet, que tu laisses tomber à la ligne d'après pour un objet déjà existant.

  4. #4
    Membre averti
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2008
    Messages
    38
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Avril 2008
    Messages : 38
    Par défaut
    OK j'ai bien compris, merci. J'ai donc utilisé clone:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     Metaphore leg= new Metaphore();
            leg=(myTable.getTabMeta()[0].clone());
     
           System.out.println(leg); 
           System.out.println(myTable.getTabMeta()[0]);
    Les adresses sont maintenant bien différentes.

    dans ma classe Metaphore:

    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
    public class Metaphore implements Cloneable{
     
        private final static int NB_FORME_MAX = 100;   //le nombre de forme maximal 
        //dans la metaphore
        //la taille de la metaphore par rapport a la taille de la fenetre3D
        private final static double TAILLE_AFFICHAGE = 0.6d;    //on cree le tableau de forme
        private Forme[] tabForme = new Forme[NB_FORME_MAX];
        private String Nom;
        //Constructeur
        public Metaphore() {
            //on initialise toutes les formes a null
            for (int i = 0; i < NB_FORME_MAX; i++) {
                this.tabForme[i] = null;
            }
        }
     
     //fonctions diverses
     
        @Override
        public Metaphore clone(){
            Metaphore meta=null;
     
            try{
                meta= (Metaphore) super.clone();
     
            }
            catch(CloneNotSupportedException cnse){
                cnse.printStackTrace(System.err);
            }
            meta.tabForme = (Forme[]) tabForme.clone(); //Clonage du tableau de forme puisque c'est ce qui m'interesse
            return meta;
        }
     
    }
    Et dans ma classe Forme :

    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
    public class Forme implements Cloneable{
        private String nom;
        private String forme;
        private String couleur;
        private float largeurForme;
        private float tailleForme;
        private float tailleMinForme;
     
        /** Creates a new instance of Forme */
        public Forme() {
        }
     
        public Forme(String nom, String forme, String couleur, float tailleForme, 
                float tailleMinForme, float largeurForme) {
     
            this.nom = nom;
            this.forme = forme;
            this.couleur = couleur;
            this.tailleForme = tailleForme;
            this.tailleMinForme = tailleMinForme;
            this.largeurForme = largeurForme;
        }
     
       //fonctions diverses
     
            @Override
        public Forme[] clone(){
            Forme[] plop=null;
     
            try{
                plop= (Forme []) super.clone();
            }
            catch(CloneNotSupportedException cnse){
                cnse.printStackTrace(System.err);
            }
     
            return plop;
        }
    }
    Voila ce que j'ai fait pourtant quand je modifie mon attribut tailleMinForme dans leg (voir dans mon premier message), ça modifie toujours celui dans myTable.getTabMeta[0].

    Je suppose qu'il doit y avoir une erreur dans mon clonage dans metaphore ou dans forme..

  5. #5
    Membre Expert

    Homme Profil pro
    Consultant informatique
    Inscrit en
    Janvier 2004
    Messages
    2 301
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Consultant informatique
    Secteur : Finance

    Informations forums :
    Inscription : Janvier 2004
    Messages : 2 301
    Par défaut
    Oui, plusieurs erreurs:

    - quand tu clone, cela ne suffit pas d'appeler super.clone. Super.clone fait le travail de clônage des champs nécessaires dans la classe parent, pas dans la classe courante. De plus, l'implémentation de Object#clone ne fait pas grand chose (mise à part instancier le type correct), donc ça sert souvent à rien d'invoquer super.clone(), à moins que tu utilises une hiérarchie d'héritage.
    - clône ne doit jamais retourner un tableau du type courant. C'est une aberration et c'est assez grave (conceptuellement, tu dis à la machine qu'à chaque demande de clône d'un objet, tu renvoies plusieurs clônes: relis comme il faut la description du contrat de clone, notament cette partie:
    Pour un objet "clonable" x :
    l'expression x.clone() != x doit renvoyer true ;

    l'expression x.clone().getClass() == x.getClass() doit renvoyer true (par convention) ;

    l'expression x.clone().equals(x) doit renvoyer true (par convention).

    Par convention, l'objet retourné doit être obtenu par un appel à la méthode super.clone() .
    Si une classe et toutes ses classes parentes (exceptée la classe Object) respectent cette convention, alors l'expression x.clone().getClass() == x.getClass() envoie bien true.
    )
    - tu dois faire un clonage en profondeur (cf mon premier post). Cela veut dire qu'il ne te suffira pas de cloner les collections, mais également leur contenu.

    Un méthode clone correctement implémentée pourrait ressembler à cela:

    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
    public class Metaphore implements Cloneable{
        private final static int NB_FORME_MAX = 100;   //le nombre de forme maximal 
        //dans la metaphore
        //la taille de la metaphore par rapport a la taille de la fenetre3D
        private final static double TAILLE_AFFICHAGE = 0.6d;    //on cree le tableau de forme
        private Forme[] tabForme = new Forme[NB_FORME_MAX];
        private String Nom;
        //Constructeur
        public Metaphore() {
            //on initialise toutes les formes a null
            for (int i = 0; i < NB_FORME_MAX; i++) {
                this.tabForme[i] = null;
            }
        }
     
        @Override
        public Metaphore clone(){
      Metaphore clone=(Metaphore)super.clone();
     
      int i=0;
      for(Forme f : tabForme) {
        if(f!=null)
          clone.tabForme[i] = (Forme) f.clone();
        i++;
      }
      clone.Nom = Nom;
     
      return clone;
        }
     
    }
    ça s'appelle un clônage en profondeur (on clône également le contenu des tableaux et des collections, et pas seulement les tableaux ou collections elle-mêmes). Utilisé également lors de copies défensives, afin de protéger les invariants d'une classe


  6. #6
    Membre averti
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2008
    Messages
    38
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Avril 2008
    Messages : 38
    Par défaut
    Ok la je comprends mieux

    Donc voila la fonction d'appel
    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
        public void displayLegend() {
            // Affichage de la légende
            Metaphore leg = new Metaphore();
     
            leg = (myTable.getTabMeta()[0].clone());
     
            System.out.println(leg);
            System.out.println(myTable.getTabMeta()[0]);
            // ici les adresses de leg et myTable..getTabMeta()[0] sont bien différentes
     
     
            int nb = leg.nbForme();
     
            for (int i = 0; i < nb; i++) {
                leg.getTabForme()[i].setLargeurForme((float) 30);
                leg.getTabForme()[i].setTailleMinForme((float) 30);
            }
            System.out.println(myTable.getTabMeta()[0].getTabForme()[0].getLargeurForme());
     
            legend.actualise(leg.sceneForme(false, true));
        }
    Ma fonction clone dans la classe métaphore:

    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
        @Override
        public Metaphore clone(){
     
            Metaphore clone=new Metaphore();
            int i = 0;
     
            try {
                clone = (Metaphore) super.clone();
            } catch (CloneNotSupportedException ex) {
               ex.printStackTrace(System.err);
            }
            for (Forme f : tabForme) {
                if (f != null) {
                    clone.tabForme[i] = f.clone();
                }
                i++;
            }
            clone.Nom = Nom;
     
            return clone;
        }
    et le clone de ma classe Forme:
    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
        public Forme clone() {
     
            Forme plop = new Forme();
     
            try {
                plop = (Forme) super.clone();
            } catch (CloneNotSupportedException ex) {
                ex.printStackTrace(System.err);
            }
     
            plop.largeurForme = largeurForme;
            plop.tailleMinForme = tailleMinForme;
            plop.couleur = couleur;
            plop.forme = forme;
            plop.tailleForme =tailleForme;
            plop.nom = nom;
     
            return plop;
        }
    Donc la a priori le clonage fonctionne puisque qd je lance en debugger leg et myTable.getTabMeta()[0] ont bien les mêmes valeurs de champs et une adresse différente.

    Pourtant lorsque je passe sur les instructions:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
                leg.getTabForme()[i].setLargeurForme((float) 30);
                leg.getTabForme()[i].setTailleMinForme((float) 30);
    Le programme modifie aussi myTable.TabMeta[0].

    Je comprends pas absolument pourquoi puisque la les adresse des 2 métaphores sont bien différentes

  7. #7
    Membre Expert

    Homme Profil pro
    Consultant informatique
    Inscrit en
    Janvier 2004
    Messages
    2 301
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Consultant informatique
    Secteur : Finance

    Informations forums :
    Inscription : Janvier 2004
    Messages : 2 301
    Par défaut
    hum ça me semble mieux, pas encore parfait mais mieux...

    -> instancier un objet et réaffecter la référence juste en dessous est pas terros du tout... pourquoi l'instancier puis réaffecter la référence juste en dessous?? si la réponse est "pour éviter qu'elle soit null lorsqu'une exception est renvoyée par clone", alors il faut mettre ce code dans le catch.
    -> catcher "CloneNotSupportedException" sans la traiter correctement ni la renvoyer plus loin est absolument horrible (même si ça fonctionne dans ton cas, c'est un mode d'utilisation des exceptions vraiment dangereux) une exception dont on ne sait pas trop quoi faire, généralement on ne fait que l'envoyer plus loin en espérant que l'appelant sait gérer le cas

    Citation Envoyé par Julgood Voir le message
    Ok la je comprends mieux

    Donc voila la fonction d'appel
    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
        public void displayLegend() {
            // Affichage de la légende
            Metaphore leg = new Metaphore(); // Met "null" à la place
    
            leg = (myTable.getTabMeta()[0].clone());
    
            System.out.println(leg);
            System.out.println(myTable.getTabMeta()[0]);
            // ici les adresses de leg et myTable..getTabMeta()[0] sont bien différentes
    
    
            int nb = leg.nbForme();
    
            for (int i = 0; i < nb; i++) {
                leg.getTabForme()[i].setLargeurForme((float) 30);
                leg.getTabForme()[i].setTailleMinForme((float) 30);
            }
            System.out.println(myTable.getTabMeta()[0].getTabForme()[0].getLargeurForme());
    
            legend.actualise(leg.sceneForme(false, true));
        }
    Ma fonction clone dans la classe métaphore:

    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
        @Override
        public Metaphore clone(){
         
            Metaphore clone=new Metaphore(); // A nouveau, initialise avec "null".
            int i = 0;
            
            try {
                clone = (Metaphore) super.clone();
            } catch (CloneNotSupportedException ex) {
               ex.printStackTrace(System.err);
               // Catcher sans renvoyer l'erreur fera qu'à la prochaine ligne, "clone" sera null et donc va certainement balancer des NullPointerException!!! à la place, fait ceci:
               throw ex;
               // ... ou alors, vire complètement ce bloc try/catch qui ne sert à rien dans ton cas
            }
            for (Forme f : tabForme) {
                if (f != null) {
                    clone.tabForme[i] = f.clone();
                }
                i++;
            }
            clone.Nom = Nom;
    
            return clone;
        }
    et le clone de ma classe Forme:
    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
        public Forme clone() {
                
            Forme plop = new Forme(); // toujours idem, "null"
            
            try {
                plop = (Forme) super.clone();
            } catch (CloneNotSupportedException ex) {
                ex.printStackTrace(System.err);
            }
            
            plop.largeurForme = largeurForme;
            plop.tailleMinForme = tailleMinForme;
            plop.couleur = couleur;
            plop.forme = forme;
            plop.tailleForme =tailleForme;
            plop.nom = nom;
            
            return plop;
        }
    Donc la a priori le clonage fonctionne puisque qd je lance en debugger leg et myTable.getTabMeta()[0] ont bien les mêmes valeurs de champs et une adresse différente.

    Pourtant lorsque je passe sur les instructions:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
                leg.getTabForme()[i].setLargeurForme((float) 30);
                leg.getTabForme()[i].setTailleMinForme((float) 30);
    Le programme modifie aussi myTable.TabMeta[0].
    Je comprends pas absolument pourquoi puisque la les adresse des 2 métaphores sont bien différentes
    C'est que quelque part, donc ton graph d'objets, tu as oublié de faire un clonage en profondeur (tu as fait une simple copie de référence).

    Mets des break point à toutes tes méthodes clone pour essayer de comprendre comment ça se passe.

    Si tu me donnes les sources complètes, je peux essayer d'exécuter et de te corriger ça. Ou alors plutôt, de le faire marcher chez moi puis de te donner des indices pour y arriver toi-même


  8. #8
    Membre averti
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2008
    Messages
    38
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Avril 2008
    Messages : 38
    Par défaut
    Bon impeccable, à force d'essayer et débugger j'ai fini par réussir!

    Je te remercie beaucoup pour ton aide!

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

Discussions similaires

  1. Problème de copie d'objet
    Par shirya dans le forum VB.NET
    Réponses: 3
    Dernier message: 07/08/2008, 16h43
  2. [FLASH MX] Problème avec l'objet Date
    Par n_tony dans le forum Flash
    Réponses: 13
    Dernier message: 22/03/2005, 14h44
  3. [VB6] Copie d'objet
    Par preverse dans le forum VB 6 et antérieur
    Réponses: 12
    Dernier message: 23/08/2004, 11h04
  4. [VB6] Copie d'objets
    Par austin49 dans le forum VB 6 et antérieur
    Réponses: 4
    Dernier message: 19/05/2003, 19h05

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