Partage
  • Partager sur Facebook
  • Partager sur Twitter

Probléme avec les classes Abstraite

Sujet résolu
    5 novembre 2008 à 14:33:03

    Bonjour,

    J'ai un soucis avec les classes abstraites.

    Bref, en suivant le tuto j'ai crée une classe Animal, elle a pour but de crée des animaux qui auront une couleur et un poid, il seront soit herbivore, soit carnivore, (j'ai donc crée 2 sous classe qui hérite d'animal) et pour finir les animaux pourront soit etre un chat ou un chien (2 filles de la classe carnivore) soit un hamster, soit un éléphant (2 filles de la classe herbivore).

    Bref voici mon programme:


    CLASSE ANIMAL:

    abstract class Animal {
    
    	protected String couleur;
    	protected int poid;
    	
    	
    	//CONSTRUCTEURS
    	public Animal()
    	{
    		couleur="aucune";
    		poid=0;
    		
    	}
    	public Animal(String pcouleur,int ppoid){
    		
    		couleur=pcouleur;
    		poid=ppoid;
    		
    	}
    	//ASSESSEUR+MUTATEUR
    	
    	public String getcouleur(){
    		
    		return couleur;
    	}
    	
    	public int getpoid(){
    		
    		return poid;
    	}
    	
    	public void setcouleur(String pcouleur){
    		
    		couleur=pcouleur;
    	}
    	
    	public void setpoid(int ppoid){
    		
    		poid=ppoid;
    	}
    
    //METHODE
    	
    	public String toString(){
    		
    		return "L'animal est de couleur "+this.couleur+" et pése "+this.poid+" kg.";
    	}
    	
    	abstract void manger();
    	
    	abstract void crier();
    	
    }
    


    CLASSE CARNIVORE:

    public abstract class Carnivore {
    
    	
    void manger(){
    		
    		System.out.println("Il est carnivore.");
    	}
    	
    }
    


    CLASSE HERBIVORE:

    public abstract class Herbivore extends Animal {
    
    	void manger(){
    		
    		System.out.println("Il est herbivore.");
    	}
    	
    	
    
    }
    


    CLASSE CHAT:

    public class Chat extends Carnivore {
    	
    	public Chat(){
    		
    		super();
    		
    		}
    	
    
    void crier(){
    		
    		System.out.println("Il miaule.C'est un chat");
    	}
    	
    }
    


    CLASSE CHIEN:

    public class Chien extends Carnivore {
    	
    	public Chien(){
    		
    		super();
    		}
    
    void crier(){
    		
    		System.out.println("Il aboie.C'est un chien.");
    	}
    	
    }
    


    CLASSE HAMSTER:

    public class Hamster extends Herbivore{
    	
    	public Hamster(){
    		super();
    	}
    	
    void crier(){
    		
    		System.out.println("Il grignote.C'est un hamster");
    	}
    
    }
    


    CLASSE ELEPHANT:

    public class Elephant {
    	public Elephant(){
    		super();
    	}
    	
    void crier(){
    		
    		System.out.println("Il trompe.C'est un élephant");
    	}
    
    }
    


    Jusque là eclipse ne détecte aucune erreur,

    Par contre au main...

    public class Main {
    
    	
    	public static void main(String[] args) {
    		
    		Chat matou=new Chat("bleu",10);//Ici il y a une erreur!
    		
    		System.out.println(matou);
    
    	}
    
    }
    


    Voila il y a une erreur lorsque je veux crée l'objet Chat, je ne comprend pas du tout pourquoi...

    en espérant que vous puissez m'aider,

    Cordialement,

    sushis
    • Partager sur Facebook
    • Partager sur Twitter
    Anonyme
      5 novembre 2008 à 16:41:00

      Il te faut un constructeur avec les paramètres (String, int) dans ta classe Chat.
      • Partager sur Facebook
      • Partager sur Twitter
        5 novembre 2008 à 20:01:59

        Je pensais qu'avec la fonction super(); cela "imporait" le constructeur de la classe mére (hors pour la classe mére de chat est Carnivore qui est elle-meme la fille de Animal).

        super() ne fonctionne pas avec les classes abstraite?
        • Partager sur Facebook
        • Partager sur Twitter
          5 novembre 2008 à 22:48:00

          Il manque "extends Animal" pour ta classe Carnivore.
          • Partager sur Facebook
          • Partager sur Twitter
            5 novembre 2008 à 23:58:14

            effectivement, mais cela ne régle pas le probleme (jai corrigé merci pour ton aide!)
            • Partager sur Facebook
            • Partager sur Twitter
              6 novembre 2008 à 1:12:02

              Je me lance dans une explication. N'hésitez pas à me corriger si je dis des âneries car je ne suis pas sûr de moi à 100%.

              Edit "après coup" :

              En fait, j'ai bien fait de dire que je n'étais pas sûr de moi à 100% car j'ai dit des bêtises. Je ferai une tentative de rectification dans un prochain message (car je souhaite comprendre pour moi-même déjà) que j'espère des gens plus calées que moi rectifieront le cas échéant. :)


              Citation : sushis

              Je pensais qu'avec la fonction super(); cela "importait" le constructeur de la classe mére (hors pour la classe mére de chat est Carnivore qui est elle-meme la fille de Animal).

              super() ne fonctionne pas avec les classes abstraite?



              Si mais le problème n'est pas là. Je pense que le nœud du problème pourrait se résumer ainsi :
              le constructeur sans argument est toujours hérité implicitement, et les constructeurs avec arguments ne sont jamais implicitement hérités. Il faut donc les définir explicitement dans chaque classe.

              En fait quand on y pense, c'est assez troublant je trouve car les constructeurs avec arguments sont à ma connaissance les seules méthodes qui ne sont pas implicitement héritées.
              D'ailleurs si quelqu'un connaît une raison pouvant expliquer la logique de cette exception ça m'intéresse beaucoup.

              En revanche, le constructeur sans argument est lui toujours hérité implicitement. Donc le

              public Chat() {
                  super();
              }
              

              dans la classe Chat (dont tu as oublié de mettre "extends Carnivore") n'est absolument pas obligatoire. Si tu ne le mets pas alors le compilateur Java le mettra automatiquement à ta place. De plus et surtout, comme les constructeurs avec arguments ne sont pas implicitement hérités, ta classe Chat n'a pas de constructeur avec arguments un point c'est tout. Elle a juste un constructeur sans argument dont de toutes façons elle aurait automatiquement hérité. Donc la création d'un objet Chat comme ça

              Chat matou=new Chat("bleu",10);
              

              donne une erreur à la compilation. D'ailleurs le message d'erreur est assez explicite :

              Main.java:6: cannot find symbol
              symbol  : constructor Chat(java.lang.String,int) 
              location: class Chat

              Pour le compilateur la classe Chat n'a pas de constructeur avec comme arguments (String,int). Il faut donc définir toi-même explicitement le constructeur avec (String, int) comme arguments, tout en utilisant quand même "super" pour faire marcher l'héritage et éviter de dupliquer du code (ce qui est le but de l'héritage). Donc, dans la classe Chat il faut mettre quelque chose du genre (pas testé) :

              public Chat(String s, int i) {
                  super(s,i);
                  // on appelle le constructeur à deux arguments de la super-classe
                  // si tu ne mets rien le compilateur met toujours super() sans
                  // argument à ta place		
              }
              

              Sauf que je me rends compte à l'instant que la super-classe c'est Carnivore et dans cette classe tu as commis la même erreur. Donc il faut faire la même cuisine dans la classe Carnivore qui ne possède pas non plus, pour l'instant, de constructeur à deux arguments.

              Voilà.
              • Partager sur Facebook
              • Partager sur Twitter
                6 novembre 2008 à 8:05:36

                Un peu long à expliquer, mais en lisant en diagonal, c'est ça (J'avais juste repérer un problème secondaire).
                • Partager sur Facebook
                • Partager sur Twitter
                  6 novembre 2008 à 9:06:16

                  Je suis désolé je crois bien avoir dit quelques bêtises. J'ai notamment dit qu'un constructeur sans argument est toujours implicitement hérité et en fait ça semble faux. Je crois bien que je suis un peu perdu dans l'héritage ou non des constructeurs.
                  • Partager sur Facebook
                  • Partager sur Twitter
                    7 novembre 2008 à 0:34:02

                    Merci beaucoup!

                    Je vais pouvoir passé aux interfaces maitenant!

                    [EDIT]:ça marche!Ton explication du début avec les implicitement j'ai pas trop capté mais le teste (le principal je pense) j'ai tout pigé.Bref mon programme fonctionne, en fait quand les variables d'instance de la classe mére, il faut chez ses filles (abstraite!) mettre un constructeur en "recréant" les variables d'instance (je m'exprime trés mal mais je ne n'ai pas trouvé d'autre mot) et ensuite les mettre en "option" dans la fonction super().et ainsi de suite sur toutes les autres sous classes!
                    Merci beaucoup pour ton aide!
                    • Partager sur Facebook
                    • Partager sur Twitter
                      7 novembre 2008 à 1:11:00

                      Ah zut ! Mauvaise manip de ma part, encore une fois. Désolé. Je voulais rééditer un de mes messages précédents.
                      N'hésitez pas à détruire ce message inutile.
                      • Partager sur Facebook
                      • Partager sur Twitter
                        8 novembre 2008 à 1:18:11

                        Bon, après quelques recherches, je tenais vraiment à rectifier les bêtises que j'avais dites plus haut. J'espère ne pas me tromper cette fois-ci et j'espère que vous n'hésiterez pas à me rectifier si c'est le cas.

                        Finalement, pour moi, il n'y a pas vraiment d'histoire d'héritage de constructeur, il y a plutôt des enchaînements d'appels de constructeurs et parfois un ajout implicite de code par le compilateur dans le corps d'une classe. Je développe. J'ai l'impression qu'il y a deux règles :

                        a) Une classe doit toujours posséder un constructeur. Et elle en possède toujours un car si le programmeur ne définit aucun constructeur dans la classe "MaClasse", et seulement dans ce cas là, alors le (gentil) compilateur prend la peine de rajouter implicitement le constructeur ci-dessous dans le corps de définition de la classe :
                        public MaClasse(){
                            super() ;
                        }
                        

                        b) Dans un constructeur, le mot clé "super" représente un constructeur de la surper-classe (constructeur qui dépend de la liste des arguments passés à "super"). Ce mot clé ne peut intervenir uniquement qu'à l'intérieur de la définition d'un constructeur et en plus seulement qu'en première instruction (sous peine d'erreur à la compilation). Quant au mot clé "this", dans le corps de définition des méthodes, il représente l'objet décrit par la classe, sauf dans un constructeur où il représente un des autres constructeurs de la même classe (constructeur qui dépend de la liste des arguments passés à "this"). Dans la définition d'un constructeur, le mot clé "this" ne peut intervenir uniquement qu'en première instruction (sous peine d'erreur à la compilation). Comme "super" et "this" doivent tous les deux être en première instruction dans un constructeur, ils ne peuvent pas coexister dans la définition d'un même constructeur.
                        Mais la première instruction du corps de définition d'un constructeur doit forcément être soit le mot clé "super", soit le mot clé "this". Et c'est toujours le cas, car si le programmeur ne commence pas la définition de son constructeur par "super" ou par "this", alors le (gentil) compilateur prend la peine de rajouter implicitement l'instruction "super() ;" au tout début de la définition du constructeur.


                        Il n'y a aucun héritage de constructeur et d'ailleurs ça n'a pas de sens de dire ça contrairement à ce que je disais plus haut dans le post. En pratique, il y a deux cas à distinguer je pense. Il y a le cas où aucun constructeur n'est défini explicitement dans le corps de la classe et le cas contraire.



                        1) Quand aucun constructeur n'est défini explicitement dans la classe

                        On applique alors la règle a). Donc en fait, cela signifie que la classe MaClasse possède automatiquement un constructeur sans paramètre qui consiste simplement à appeler le constructeur sans paramètre de sa super-classe, sachant que cela produira une erreur si la super-classe n'a pas de constructeur sans paramètre. La super-classe peut être une classe définie par le programmeur ou bien être carrément la classe Object, ça ne change pas le principe.
                        On serait tenté de dire que la classe MaClasse "hérite" du constructeur sans paramètre de sa super-classe mais ça serait un abus de langage car il y a bel et bien la création d'une nouvelle méthode portant le nom "MaClasse" qui n'existait dans aucune des classes parentes (alors qu'avec l'héritage une classe récupère une méthode déjà existante dans une des classes parentes).

                        2) Quand au moins un constructeur est défini explicitement dans la classe

                        C'est la règle b) qui va s'appliquer et donc, qu'on le veuille ou non, après des ajouts éventuels du compilateur, tous les constructeurs commenceront soit par "this" soit par "super". Donc un constructeur appellera soit un autre constructeur de la même classe, soit un constructeur de la super-classe.
                        Un cas intéressant est celui où la classe "MaClasse" admet une super-classe qui n'a pas de constructeur sans paramètre. Dans ce cas, le programmeur devra placer lui même "this" ou "super", avec des arguments, au début de tous les constructeurs de "MaClasse" car sinon il y aura une erreur de compilation. En effet, si le programmeur écrit un constructeur de la forme :
                        public MaClasse(<éventuellement des paramètres>){
                            // code sans "this" et sans "super"
                            // code sans "this" et sans "super"
                            // code sans "this" et sans "super"
                        }
                        

                        Alors le compilateur ajoute au début l'instruction "super() ;" c'est-à-dire l'appel du constructeur sans paramètre de la super-classe. Or, on s'est placé dans le cas où la super-classe n'a pas de constructeur sans paramètre. Donc cela entraînera une erreur de compilation.




                        Ce qui est intéressant, c'est que si une classe B est une sous classe de A, elle-même sous classe de Object, alors toutes ces règles sont faites pour que la construction d'un objet obj de la classe B provoque l'exécution de tous les constructeurs en allant dans l'ordre suivant :
                        1) exécution du constructeur de Object (la couche interne Object de l'objet obj est créée)
                        2) exécution d'un ou du constructeur de A (la sur-couche A de l'objet obj est créée)
                        3) exécution d'un ou du constructeur de B (la sur-sur-couche B de l'objet obj est créée)
                        On peut voir l'objet comme un boule avec des couches stratifiées dont la plus interne est la couche Object.

                        En effet, avec l'instruction "B obj = new B(a,b) ;" c'est le constructeur B(a,b) de la classe B qui est appelé et donc posé sur la pile d'appel de méthodes. Mais soit ce constructeur commence par "this" soit il commence par "super". S'il commence par "this", alors un autre constructeur (pas avec la même signature) de la classe B est appelé et mis sur la pile d'appel. À ce petit jeu là, on finira forcément par tomber sur un constructeur de B qui commence par "super" et c'est alors un constructeur de la classe A qui est appelé et mis sur la pile d'appel. Jusqu'à ce qu'on arrive à l'appel du constructeur de la classe Objet qui est donc le dernier constructeur posé sur la pile d'appel (fin de l'empilement). C'est donc lui qui est au-dessus de la pile et c'est donc lui qui est exécuté en premier et ensuite enlevé de la pile. Et ainsi de suite, l'exécution des constructeurs est dans le même ordre que celui du dépilement des constructeurs sur la pile d'appel, c'est-à-dire dans l'ordre inverse de l'empilement et on retrouve le "1), 2), 3)" cité juste au dessus.

                        Voilà, j'espère que j'ai pas dit de bêtise cette fois. Surtout n'hésiter à rectifier.
                        • Partager sur Facebook
                        • Partager sur Twitter

                        Probléme avec les classes Abstraite

                        × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                        × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                        • Editeur
                        • Markdown