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 :

Cloner un objet


Sujet :

Langage Java

  1. #1
    Membre éclairé
    Profil pro
    Inscrit en
    Mai 2007
    Messages
    406
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2007
    Messages : 406
    Par défaut Cloner un objet
    Bonjour à tous !

    J'ai un petit problème pour cloner un objet. J'ai lu le tuto de Yann D'Isanto dans les cours de Java , mais rien n'y fait.

    J'ai plusieurs questions :

    - Est-ce que l'on peut cloner un objet dans une méthode de la classe de cet objet ? (une méthode qui appartient à cet objet) ou il faut le faire dans une classe extérieure ? (ce qui m'arrangerait pas du tout)

    - Et ensuite, voici mon problème. J'ai l'impression que le clone de mon objet Echiquier ne marche pas, pourtant, aucune exception ne survient pendant l'opération. En fait, il attribue mon objet Echiquier à l'objet EchiquierCloné, c'est à dire qu'il modifie l'objet original.

    Voici mon code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    public boolean uneMethode() {
    ...
    // TODO: Le clone de l'échiquier marche pas ! Ca modifie le véritable échiquier ?!
    // On clone l'échiquier
    Echiquier echiquierClone = (Echiquier) this.clone();
    // On joue un coup virtuellement
    echiquierClone.joueCoup(position, autrePosition);
    ...
    }
    Cette méthode si situe dans ma classe Echiquier, et cette classe étend Cloneable.
    Voici les attributs principaux de ma classe Echiquier :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    // La taille du plateau est une constante
    public static final int TAILLE_PLATEAU = 8;
    // L'échiquier possède un tableau de cases
    private Case plateau[][] = new Case[TAILLE_PLATEAU][TAILLE_PLATEAU];
    Et ce tableau de Cases est initialisé comme ça dans mon constructeur :
    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
     
    int camp;  // 0 => blanc ; 1 => noir
    for(camp = 0; camp < 2; camp++) {
        final int dernierNumero = TAILLE_PLATEAU - 1;
        final int ligneArriere = camp * dernierNumero; 
        final int ligneAvant = 1 + camp * (TAILLE_PLATEAU - 3); 
        final boolean couleurNoire = (camp == 1); 
     
        // Figures
        plateau[ligneArriere][0] = new Case(ligneArriere, 0, new Tour(couleurNoire, "T")); 
        plateau[ligneArriere][7] = new Case(ligneArriere, 7, new Tour(couleurNoire, "T"));
        plateau[ligneArriere][2] = new Case(ligneArriere, 2, new Fou(couleurNoire, "F"));
        plateau[ligneArriere][5] = new Case(ligneArriere, 5, new Fou(couleurNoire, "F"));
        plateau[ligneArriere][3] = new Case(ligneArriere, 3, new Dame(couleurNoire, "D"));
        plateau[ligneArriere][4] = new Case(ligneArriere, 4, new Roi(couleurNoire, "R"));
        plateau[ligneArriere][1] = new Case(ligneArriere, 1, new Cavalier(couleurNoire, "C"));
        plateau[ligneArriere][6] = new Case(ligneArriere, 6, new Cavalier(couleurNoire, "C"));
        // Pions
        plateau[ligneAvant][0] = new Case(ligneAvant, 0, new Pion(couleurNoire, ""));
        plateau[ligneAvant][1] = new Case(ligneAvant, 1, new Pion(couleurNoire, ""));
        plateau[ligneAvant][2] = new Case(ligneAvant, 2, new Pion(couleurNoire, ""));
        plateau[ligneAvant][3] = new Case(ligneAvant, 3, new Pion(couleurNoire, ""));
        plateau[ligneAvant][4] = new Case(ligneAvant, 4, new Pion(couleurNoire, ""));
        plateau[ligneAvant][5] = new Case(ligneAvant, 5, new Pion(couleurNoire, ""));
        plateau[ligneAvant][6] = new Case(ligneAvant, 6, new Pion(couleurNoire, ""));
        plateau[ligneAvant][7] = new Case(ligneAvant, 7, new Pion(couleurNoire, ""));
     
         // Aperçu du tableau de positions des pièces
         //  ...   ...   ...  ...
         // (2,0) (2,1) (2,2) ...
         // (1,0) (1,1) (1,2) ...
         // (0,0) (0,1) (0,2) ...
    }
    Et enfin, voici la méthode clone() dans toujours la même classe (Echiquier) :
    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
     
    // Clone l'objet echiquier
        // TODO: Marche pas !
        @Override
        public Object clone() {
            Echiquier echiquier = null;
            try {
                echiquier = (Echiquier) super.clone();
            } catch (CloneNotSupportedException exception) {
                System.err.println("" + exception.getMessage());
            }
            // On clone le plateau de jeu
            echiquier.plateau = (Case[][]) plateau.clone();
            // TODO: Il faut peut être ici cloné aussi les pièces du plateau
            // mais comment faire ?
     
            // On retourne l'échiquier cloné
            return echiquier;
        }
    -Est ce que les classes Case, Pion, Tour, Fou, Dame, Roi et Cavalier doivent aussi étendre Cloneable ? (Ce n'est pas le cas pour l'instant)

    Merci pour les personnes qui prendront le temps de regarder mon portable et me proposer une solution !

  2. #2
    Membre éprouvé
    Profil pro
    Inscrit en
    Octobre 2005
    Messages
    961
    Détails du profil
    Informations personnelles :
    Âge : 45
    Localisation : France

    Informations forums :
    Inscription : Octobre 2005
    Messages : 961
    Par défaut
    Je pense que vous devriez parcourir "plateau" pour cloner les "Case" une par une, car les celles-ci contiennent des données spécifiques à un échiquier. Pour Pion, Tour, Fou, Dame, Roi et Cavalier, celà dépend : si elles contiennent des données spécifiques à un échiquier, alors oui.

  3. #3
    Membre éclairé
    Profil pro
    Inscrit en
    Mai 2007
    Messages
    406
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2007
    Messages : 406
    Par défaut
    Voici de nouveau ma méthode clone() de la classe Echiquier :
    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
    // Clone l'objet echiquier
        // TODO: Marche pas !
        @Override
        public Object clone() {
            Echiquier echiquier = null;
            try {
                echiquier = (Echiquier) super.clone();
            } catch (CloneNotSupportedException exception) {
                System.err.println("" + exception.getMessage());
            }
            // On clone le plateau de jeu
            echiquier.plateau = (Case[][]) plateau.clone();
            // TODO: Il faut peut être ici cloné aussi les pièces du plateau
            for (int i = 0; i < TAILLE_PLATEAU; i++) {
                for (int j = 0; j < TAILLE_PLATEAU; j++) {
                    echiquier.plateau[i][j] = (Case) plateau[i][j].clone();
                }
            }
            // On retourne l'échiquier cloné
            return echiquier;
        }
    Mais si je fais ça, que la classe Case implémente l'interface Cloneable ou non, ça me fait une erreur pour la ligne en gras :
    "clone() has protected access in java.lang.Object"
    Pourquoi cette erreur ??

  4. #4
    Membre éprouvé
    Profil pro
    Inscrit en
    Octobre 2005
    Messages
    961
    Détails du profil
    Informations personnelles :
    Âge : 45
    Localisation : France

    Informations forums :
    Inscription : Octobre 2005
    Messages : 961
    Par défaut
    Est-ce que Case implémente l'interface Cloneable?

    Citation Envoyé par womannosky Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    (Case) plateau[i][j].clone();
    Un peu ambigu comme écriture, mieux vaut ajouter les parenthèses :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    ((Case) plateau[i][j]).clone();
    Au fait, quel est le type de plateau?

    (Note : j'ai modifié mon message précédent au moment où vous postiez.)

  5. #5
    Membre éclairé
    Profil pro
    Inscrit en
    Mai 2007
    Messages
    406
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2007
    Messages : 406
    Par défaut
    Citation Envoyé par BugFactory Voir le message
    Je pense que vous devriez parcourir "plateau" pour cloner les "Case" une par une, car les celles-ci contiennent des données spécifiques à un échiquier. Pour Pion, Tour, Fou, Dame, Roi et Cavalier, celà dépend : si elles contiennent des données spécifiques à un échiquier, alors oui.
    Ok, je veux bien mais comment faire ? Pouvez vous m'aider ?

  6. #6
    Membre éclairé
    Profil pro
    Inscrit en
    Mai 2007
    Messages
    406
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2007
    Messages : 406
    Par défaut
    Ok pour votre dernier message.
    Case n'implémente pas l'interface Cloneable pour l'instant, il le faut ?

    Votre écriture comme ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    ((Case) plateau[i][j]).clone();
    Ca ne fonctionne pas, j'ai le même message

    Et plateau est de type Case[][].

    Merci pour votre aide

  7. #7
    Membre éclairé
    Profil pro
    Inscrit en
    Juillet 2007
    Messages
    802
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2007
    Messages : 802
    Par défaut
    Oui. Non seulement Case doit implémenter Cloneable, mais en plus tu dois redéfinir la méthode clone() héritée de Object pour cloner tous les attributs complexes s'il y en a et relever la visibilité à public.

  8. #8
    Membre éclairé
    Profil pro
    Inscrit en
    Mai 2007
    Messages
    406
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2007
    Messages : 406
    Par défaut
    Ok, Verbose, merci !
    Je viens donc d'implémenter Cloneable dans ma classe Case, et d'y ajouter ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    // Clone la case
        @Override
        public Object clone() {
            Case caseClone = null;
            try {
                caseClone = (Case) super.clone();
            } catch (CloneNotSupportedException exception) {
                System.err.println("" + exception.getMessage());
            }
            // On retourne la case cloné
            return caseClone;
        }
    Grâce à cette méthode, je n'ai plus l'erreur de tout à l'heure.

    Et comme les cases contiennent des pièces, il faut que je fasse pareil pour la classe Piece ??
    Si oui, ça va être interminable...

  9. #9
    Membre éclairé
    Profil pro
    Inscrit en
    Juillet 2007
    Messages
    802
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2007
    Messages : 802
    Par défaut
    Oui, il faut aussi que tu clones les Pieces

  10. #10
    Membre éclairé
    Profil pro
    Inscrit en
    Mai 2007
    Messages
    406
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2007
    Messages : 406
    Par défaut
    Ma classe échiquier contient un plateau de cases qui contiennent chacune une pièce.
    Voici mes trois méthodes clone() :
    - Classe Echiquier :
    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
     
    // Clone l'objet echiquier
        // TODO: Marche pas !
        @Override
        public Object clone() {
            Echiquier echiquierClone = null;
            try {
                echiquierClone = (Echiquier) super.clone();
            } catch (CloneNotSupportedException exception) {
                System.err.println("" + exception.getMessage());
            }
            // On clone le plateau de jeu
            echiquierClone.plateau = (Case[][]) plateau.clone();
     
            // Il faut cloner aussi les pièces du plateau
            for (int i = 0; i < TAILLE_PLATEAU; i++) {
                for (int j = 0; j < TAILLE_PLATEAU; j++) {
                    echiquierClone.plateau[i][j] = (Case) ((Case) plateau[i][j]).clone();
                }
            }
            // On retourne l'échiquier cloné
            return echiquierClone;
        }
    - Classe Case :
    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
     
    // Clone la case
        @Override
        public Object clone() {
            Case caseClone = null;
            try {
                caseClone = (Case) super.clone();
            } catch (CloneNotSupportedException exception) {
                System.err.println("" + exception.getMessage());
            }
            // S'il y a une pièce sur la case
            if (piece != null) {
                // On clone la pièce de la case
                caseClone.piece = (Piece) piece.clone();
            }
     
            // On retourne la case cloné
            return caseClone;
        }
    - Classe Piece :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    // Clone la pièce
        @Override
        public Object clone() {
            Piece pieceClone = null;
            try {
                pieceClone = (Piece) super.clone();
            } catch (CloneNotSupportedException exception) {
                System.err.println("" + exception.getMessage());
            }
            // On retourne la case cloné
            return pieceClone;
        }
    Mes trois classes implémentent l'interface Cloneable, mais celà ne marche toujours pas !
    Quand je veux cloner mon échiquier comme ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    // TODO: Le clone de l'échiquier marche pas ! Ca modifie le véritable échiquier ?!
    // On clone l'échiquier
    Echiquier echiquierClone = (Echiquier) this.clone(); // this car je suis dans la classe Echiquier
    // On joue le coup sur l'échiquier cloné pour pouvoir tester le chemin libre ensuite
    echiquierClone.joueCoup(position, positionAutre);
    Et bien, ca me fait une erreur car l'échiquier que je veux cloner est attribué à l'échiquier cloné, donc quand je modifie l'échiquier cloné, je modifie en même temps l'échiquier de base que je ne veux pas cloner !

    Ma ligne en gras est-elle correcte sachant que je clone l'objet en cours (this) ?

  11. #11
    Expert éminent
    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
    Billets dans le blog
    1
    Par défaut
    Salut,


    Citation Envoyé par womannosky Voir le message
    Et comme les cases contiennent des pièces, il faut que je fasse pareil pour la classe Piece ??
    Si oui, ça va être interminable...
    Oui : si tu veux cloner des objets, il faut bien implémenter leurs méthodes clone(). Sinon tu partageras les mêmes instances et les modifications impacteront les deux instances...



    Ton problème vient que pour les tableaux, il faut cloner TOUS les éléments, y compris les sous-tableaux :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    // On clone le tableau :
    echiquier.plateau = echiquier.plateau.clone();
    for (int i = 0; i < TAILLE_PLATEAU; i++) {
    	// Les sous-tableaux
    	echiquier.plateau[i] = echiquier.plateau[i].clone();
    	for (int j = 0; j < TAILLE_PLATEAU; j++) {
    		// ainsi que chaque éléments :
    		echiquier.plateau[i][j] = (Case) plateau[i][j].clone();
    	}
    }




    Sinon j'aurais deux conseils par rapport à ton code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
        @Override
        public Object clone() {
            Case caseClone = null;
            try {
                caseClone = (Case) super.clone();
            } catch (CloneNotSupportedException exception) {
                System.err.println("" + exception.getMessage());
            }
            // On retourne la case cloné
            return caseClone;
        }
    1/ Afficher un message c'est bien... mais pas très pratique car pas forcément très lisible...
    Théoriquement tu ne devrais jamais avoir de CloneNotSupportedException, sauf si tu n'implémentes pas Cloneable. Donc l'exception pourrait être ignoré avec un simple message comme ici, mais cela peut s'avérer peu pratique !

    Si l'exception remonte par un malheureux hasard, tu va te contenter d'afficher un message sur la console, et ta méthode clone() va continuer normalement en retournant null.
    Du coup ton programme va planter avec un NullPointerException peu explicite, et pour peu que le message précédent passe inaperçu tu vas perdre du temps à en rechercher la cause...


    Perso je préfère les codes "fail-fast", litérallement : qui plante rapidement.
    C'est à dire que plutôt que d'afficher une message et de tenter de continuer comme si de rien n'était, je préfère remonter une exception immédiatement :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    } catch (CloneNotSupportedException exception) {
    	throw new RuntimeException(exception);
    }
    Au moins j'obtiens un stacktrace complet qui me donne toutes les infos neccessaire
    La RuntimeException permet de faire planter le programme sans obliger le développeur à les traiter spécifiquement.
    Bref CloneNotSupportedException n'est plus une exception qu'on ignore, mais un état invalide qui provoquera un plantage (qui pourra être corriger plus rapidement).





    2/ Depuis Java 5 on peut retourner un type différent de celui déclarer dans la classe parente : c'est la covariance (lire : Quelles sont les règles à respecter pour redéfinir/implémenter une méthode ?).
    Cela permet d'éviter de s'embêter avec de multiple cast.

    Au final cela donnerait :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
        @Override
        public Case clone() {
            try {
                Case caseClone = (Case) super.clone();
     
                // Traitement éventuel des attributs
     
                return caseClone;
            } catch (CloneNotSupportedException exception) {
                throw new RuntimeException(e);
            }
        }

    a++

    PS : Attention également aux références croisées, qui peuvent faire partir le tout en boucle infini...

  12. #12
    Membre éclairé
    Profil pro
    Inscrit en
    Mai 2007
    Messages
    406
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2007
    Messages : 406
    Par défaut
    AdiGuba, toujours aussi efficace. Merci pour tes réponses multiples, je vais tenir compte de tes conseils. Surtout que mon code ne plante plus, il me fait un truc bizarre mais bon, ça doit venir de moi. Merci !

    Pour le :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    throw new RuntimeException(exception);
    Je peux l'utiliser dans tous mes catch () ??

    Mon échiquier contient une liste de coups :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    private List<Coup> listeCoups = new ArrayList<Coup>();
    Et je voudrais cloner cet objet List, comment faire pour ça ??

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    echiquierClone.listeCoups = (List<Coup>) listeCoups.clone();
    Ca ne marche pas

  13. #13
    Membre éclairé
    Profil pro
    Inscrit en
    Mai 2007
    Messages
    406
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2007
    Messages : 406
    Par défaut
    J'ai trouvé seul pour cloner une List. Voici le code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    echiquier.listeCoups = new ArrayList<Coup>(listeCoups);
    A+

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

Discussions similaires

  1. Comment cloner un objet ?
    Par rei.uchiwa dans le forum Langage
    Réponses: 2
    Dernier message: 29/04/2010, 22h48
  2. Cloner un objet
    Par jeandadaf dans le forum VB.NET
    Réponses: 10
    Dernier message: 18/10/2009, 14h26
  3. [POO] cloner un objet (et tous ses sous objets ?)
    Par Merfolk dans le forum Langage
    Réponses: 11
    Dernier message: 05/05/2008, 16h23
  4. [WD9] Cloner un objet
    Par Romanops dans le forum WinDev
    Réponses: 12
    Dernier message: 06/02/2006, 15h52
  5. Cloner un objet : comment créer l'instance ?
    Par phplive dans le forum Langage
    Réponses: 8
    Dernier message: 29/05/2005, 18h27

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