Java Héritage Multiple

dans une tentative de comprendre pleinement comment résoudre les multiples problèmes d'héritage de Java, j'ai une question classique que j'ai besoin de clarifier.

disons que j'ai la classe Animal cela a les sous-classes Bird et Horse et je dois faire une classe Pegasus qui s'étend de Bird et Horse depuis Pegasus est à la fois un oiseau et un cheval.

je pense que c'est le problème classique du diamant. De ce que je peux comprendre la façon classique de résoudre cela est de faire les Animal , Bird et Horse interfaces de classes et d'implémenter Pegasus d'eux.

je me demandais s'il y avait un autre moyen de résoudre le problème dans lequel je peux encore créer des objets pour les oiseaux et les chevaux. S'il y avait un moyen d'être capable de créer des animaux aussi ce serait grand, mais pas nécessaire.

160
demandé sur Tim B 2014-02-17 12:46:53

16 réponses

vous pourriez créer des interfaces pour les classes d'animaux (classe au sens biologique), comme public interface Equidae pour les chevaux et public interface Avialae pour les oiseaux (Je ne suis pas biologiste, donc les Termes peuvent être erronés).

alors vous pouvez encore créer un

public class Bird implements Avialae {
}

et

public class Horse implements Equidae {}

et aussi

public class Pegasus implements Avialae, Equidae {}

ajoutant des commentaires:

afin de réduisez le code dupliqué, vous pouvez créer une classe abstraite qui contient la plupart du code commun des animaux que vous voulez implémenter.

public abstract class AbstractHorse implements Equidae {}

public class Horse extends AbstractHorse {}

public class Pegasus extends AbstractHorse implements Avialae {}

mise à Jour

j'aimerais ajouter un détail. Comme Brian remarque , c'est quelque chose que L'OP savait déjà.

cependant, je tiens à souligner, que je suggère de contourner le problème "multi-héritage" avec les interfaces et que Je ne recommande pas d'utiliser des interfaces qui représentent déjà un type concret (comme Bird) mais plutôt un comportement (d'autres font référence à duck-typing, ce qui est bon, aussi, mais je veux dire juste: la classe biologique des oiseaux, Avialae). Je ne recommande pas non plus d'utiliser des noms d'interface commençant par un "I" majuscule, comme IBird , qui ne dit tout simplement rien sur la raison pour laquelle vous avez besoin d'une interface. C'est la différence par rapport à la question: construire la hiérarchie de l'héritage en utilisant des interfaces, utiliser des classes abstraites quand utile, mettre en œuvre des classes concrètes si nécessaire et utiliser la délégation si nécessaire.

112
répondu Moritz Petersen 2017-05-23 11:47:15

il existe deux approches fondamentales pour combiner des objets ensemble:

  • le premier est héritage . Comme vous avez déjà identifié les limites de l'héritage signifie que vous ne pouvez pas faire ce dont vous avez besoin ici.
  • la seconde est Composition . Depuis l'héritage a échoué, vous devez utiliser la composition.

La façon dont cela fonctionne est que vous avoir un objet Animal. À l'intérieur de cet objet, vous ajoutez d'autres objets qui donnent les propriétés et les comportements dont vous avez besoin.

par exemple:

  • Oiseau s'étend Animal met en œuvre IFlier
  • Cheval s'étend Animal met en œuvre IHerbivore, IQuadruped
  • Pegasus extends Animal implements IHerbivore, Iquaduped, IFlier

Maintenant IFlier ressemble à ceci:

 interface IFlier {
     Flier getFlier();
 }

Donc Bird ressemble à ceci:

 class Bird extends Animal implements IFlier {
      Flier flier = new Flier();
      public Flier getFlier() { return flier; }
 }

Maintenant, vous avez tous les avantages de l'Héritage. Vous pouvez réutiliser le code. Vous pouvez avoir une collection de IFliers, et peut utiliser tous les autres avantages du polymorphisme, etc.

cependant vous avez aussi toute la flexibilité de la Composition. Vous pouvez appliquer autant d'interfaces différentes et la classe de renfort composite que vous voulez à chaque type de Animal - avec autant de contrôle que vous avez besoin sur la façon dont chaque bit est configuré.

schéma de stratégie approche alternative de la composition

une approche alternative selon ce que vous faites et comment vous le faites est d'avoir la classe de base Animal contenant une collection interne pour garder la liste des différents comportements. Dans ce cas, vous finissez par utiliser quelque chose plus proche du modèle de stratégie. Cela donne des avantages en termes de simplification du code (par exemple Horse n'a pas besoin de savoir quoi que ce soit au sujet de Quadruped ou Herbivore ) mais si vous ne faites pas aussi l'approche d'interface vous perdez beaucoup des avantages du polymorphisme, etc.

87
répondu Tim B 2014-03-28 21:40:24

j'ai une idée stupide:

public class Pegasus {
    private Horse horseFeatures; 
    private Bird birdFeatures; 

   public Pegasus(Horse horse, Bird bird) {
     this.horseFeatures = horse;
     this.birdFeatures = bird;
   }

  public void jump() {
    horseFeatures.jump();
  }

  public void fly() {
    birdFeatures.fly();
  }
}
41
répondu Pavel Janicek 2014-04-22 13:15:29

puis-je suggérer le concept de Duck-typing ?

très probablement, vous auriez tendance à faire le Pegasus étendre un oiseau et une interface de cheval, mais le type de canard suggère en fait que vous devriez plutôt hériter comportement . Comme déjà dit dans les commentaires, un pegasus n'est pas un oiseau, mais il peut voler. Donc votre Pegasus devrait plutôt hériter d'une interface Flyable et disons une interface Gallopable .

ce type de concept est utilisé dans le schéma de stratégie . L'exemple donné vous montre en fait comment un canard hérite du FlyBehaviour et du QuackBehaviour et peut encore y avoir des canards, par exemple le RubberDuck , qui ne peuvent pas voler. Ils auraient pu faire aussi le Duck étendre un Bird - classe, mais alors ils auraient renoncé à une certaine flexibilité, parce que chaque Duck serait en mesure de voler, même les pauvres RubberDuck .

25
répondu snrlx 2017-04-12 07:31:17

techniquement parlant, vous ne pouvez étendre qu'une classe à la fois et implémenter plusieurs interfaces, mais en mettant la main sur l'ingénierie logicielle, je préférerais proposer une solution spécifique au problème qui ne répond généralement pas. D'ailleurs, c'est une bonne pratique, pas d'étendre les classes concrètes/seulement étendre les classes abstraites pour prévenir les comportements héréditaires indésirables - il n'y a pas une telle chose comme un "animal" et pas d'utilisation d'un objet animal, mais seulement des animaux concrets.

18
répondu Smutje 2014-02-17 08:55:32

il est sûr de garder un cheval dans une étable avec une demi-porte, comme un cheval ne peut pas passer plus d'une demi-porte. Par conséquent, j'ai mis en place un service de logement de chevaux qui accepte tout élément de type cheval et le met dans une étable avec une demi-porte.

est-ce qu'un cheval est comme un animal qui peut voler, même un cheval?

j'avais l'habitude de penser beaucoup à l'héritage multiple, mais maintenant que j'ai été programmer pendant plus de 15 ans, Je ne me soucie plus de mettre en œuvre plusieurs héritage.

plus souvent qu'autrement, quand j'ai essayé de faire face à un design qui pointait vers l'héritage multiple, je suis venu plus tard pour libérer que j'avais manqué compris le domaine du problème.

ou

S'il ressemble à un canard et fait Coin-coin comme un canard mais il a besoin batteries, vous avez probablement la mauvaise abstraction .

12
répondu Ian Ringrose 2014-02-27 14:30:40

en Java 8, qui est encore dans la phase de développement à partir de février 2014, vous pouvez utiliser méthodes par défaut pour obtenir une sorte de C++-comme l'héritage multiple. Vous pouvez aussi jeter un oeil à ce tutoriel qui montre quelques exemples qui devraient être plus faciles à démarrer que la documentation officielle.

12
répondu GOTO 0 2017-02-14 13:33:35

Java n'a pas de problème d'héritage Multiple, car il n'a pas d'héritage multiple. C'est à dessein, afin de résoudre le véritable problème de l'héritage multiple (le problème du diamant).

Il existe différentes stratégies pour atténuer le problème. Le plus immédiatement réalisable étant l'objet Composite que Pavel suggère (essentiellement comment C++ le gère). Je ne sais pas si l'héritage multiple via la linéarisation C3 (ou similaire) est sur les cartes pour Java, mais j'en doute.

si votre question Est académique, alors la bonne solution est que L'oiseau et le cheval sont plus concrets, et il est faux de supposer qu'un Pégase est simplement un oiseau et un cheval combinés. Il serait plus exact de dire qu'un Pegasus a certaines propriétés intrinsèques en commun avec les Oiseaux et les Chevaux (c'est qu'ils ont peut-être des ancêtres communs). Cela peut être suffisamment modélisé comme Moritz réponse.

8
répondu Mikkel Løkke 2014-02-17 14:46:06

je pense que cela dépend beaucoup de vos besoins, et de la façon dont vos classes d'animaux doivent être utilisées dans votre code.

si vous voulez pouvoir utiliser les méthodes et les caractéristiques de vos implémentations de chevaux et D'oiseaux dans votre classe Pegasus, alors vous pouvez implémenter Pegasus comme une composition d'un oiseau et D'un cheval:

public class Animals {

    public interface Animal{
        public int getNumberOfLegs();
        public boolean canFly();
        public boolean canBeRidden();
    }

    public interface Bird extends Animal{
        public void doSomeBirdThing();
    }
    public interface Horse extends Animal{
        public void doSomeHorseThing();
    }
    public interface Pegasus extends Bird,Horse{

    }

    public abstract class AnimalImpl implements Animal{
        private final int numberOfLegs;

        public AnimalImpl(int numberOfLegs) {
            super();
            this.numberOfLegs = numberOfLegs;
        }

        @Override
        public int getNumberOfLegs() {
            return numberOfLegs;
        }
    }

    public class BirdImpl extends AnimalImpl implements Bird{

        public BirdImpl() {
            super(2);
        }

        @Override
        public boolean canFly() {
            return true;
        }

        @Override
        public boolean canBeRidden() {
            return false;
        }

        @Override
        public void doSomeBirdThing() {
            System.out.println("doing some bird thing...");
        }

    }

    public class HorseImpl extends AnimalImpl implements Horse{

        public HorseImpl() {
            super(4);
        }

        @Override
        public boolean canFly() {
            return false;
        }

        @Override
        public boolean canBeRidden() {
            return true;
        }

        @Override
        public void doSomeHorseThing() {
            System.out.println("doing some horse thing...");
        }

    }

    public class PegasusImpl implements Pegasus{

        private final Horse horse = new HorseImpl();
        private final Bird bird = new BirdImpl();


        @Override
        public void doSomeBirdThing() {
            bird.doSomeBirdThing();
        }

        @Override
        public int getNumberOfLegs() {
            return horse.getNumberOfLegs();
        }

        @Override
        public void doSomeHorseThing() {
            horse.doSomeHorseThing();
        }


        @Override
        public boolean canFly() {
            return true;
        }

        @Override
        public boolean canBeRidden() {
            return true;
        }
    }
}

une autre possibilité est d'utiliser un entité-composant-système approche au lieu d'héritage pour définir vos animaux. Bien sûr, cela signifie que vous n'aurez pas de classes Java individuelles des animaux, mais qu'ils ne seront définis que par leurs composants.

un pseudo-code pour une approche entité-composant-système pourrait ressembler à ceci:

public void createHorse(Entity entity){
    entity.setComponent(NUMER_OF_LEGS, 4);
    entity.setComponent(CAN_FLY, false);
    entity.setComponent(CAN_BE_RIDDEN, true);
    entity.setComponent(SOME_HORSE_FUNCTIONALITY, new HorseFunction());
}

public void createBird(Entity entity){
    entity.setComponent(NUMER_OF_LEGS, 2);
    entity.setComponent(CAN_FLY, true);
    entity.setComponent(CAN_BE_RIDDEN, false);
    entity.setComponent(SOME_BIRD_FUNCTIONALITY, new BirdFunction());
}

public void createPegasus(Entity entity){
    createHorse(entity);
    createBird(entity);
    entity.setComponent(CAN_BE_RIDDEN, true);
}
6
répondu Balder 2014-02-17 09:52:15

vous pouvez avoir une hiérarchie d'interface et ensuite étendre vos classes à partir d'interfaces sélectionnées:

public interface IAnimal {
}

public interface IBird implements IAnimal {
}

public  interface IHorse implements IAnimal {
}

public interface IPegasus implements IBird,IHorse{
}

et ensuite définir vos classes selon les besoins, en étendant une interface spécifique:

public class Bird implements IBird {
}

public class Horse implements IHorse{
}

public class Pegasus implements IPegasus {
}
4
répondu richardtz 2014-02-17 09:07:46

Ehm, votre classe peut être la sous-classe pour seulement 1 autre, mais tout de même, vous pouvez avoir autant d'interfaces mises en œuvre, comme vous le souhaitez.

Pegasus est en fait un cheval (c'est un cas particulier d'un cheval), qui est capable de voler (qui est la "compétence" de cette spéciale de cheval). D'autre part, vous pouvez dire, le Pegasus est un oiseau, qui est capable de marcher et est 4legged - tout dépend comment il est plus facile pour vous d'écrire le code.

Comme dans votre cas, vous pouvez dis:

abstract class Animal {
   private Integer hp = 0; 
   public void eat() { 
      hp++; 
   }
}
interface AirCompatible { 
   public void fly(); 
}
class Bird extends Animal implements AirCompatible { 
   @Override
   public void fly() {  
       //Do something useful
   }
} 
class Horse extends Animal {
   @Override
   public void eat() { 
      hp+=2; 
   }

}
class Pegasus extends Horse implements AirCompatible {
   //now every time when your Pegasus eats, will receive +2 hp  
   @Override
   public void fly() {  
       //Do something useful
   }
}
4
répondu András Iványi 2014-06-07 08:39:41
Les Interfaces

ne simulent pas les héritages multiples. Les créateurs de Java considèrent que l'héritage multiple est une erreur, il n'y a donc pas de telle chose en Java.

si vous voulez combiner la fonctionnalité de deux classes dans la composition d'un objet à usage unique. C'est-à-dire:

public class Main {
    private Component1 component1 = new Component1();    
    private Component2 component2 = new Component2();
}

et si vous voulez exposer certaines méthodes, définissez-les et laissez-les déléguer l'appel au contrôleur correspondant.

ici, les interfaces peuvent être utiles-si Component1 implémente l'interface Interface1 et Component2 met en œuvre Interface2 , vous pouvez définir

class Main implements Interface1, Interface2

de sorte que vous pouvez utiliser les objets de façon interchangeable lorsque le contexte le permet.

donc de mon point de vue, vous ne pouvez pas entrer dans diamond problem.

3
répondu Karthik Surianarayanan 2014-02-17 08:59:07

comme vous le savez déjà, l'héritage multiple de classes en Java n'est pas possible, mais c'est possible avec des interfaces. Vous pouvez également envisager d'utiliser le modèle de conception de la composition.

j'ai écrit un article très complet sur la composition il y a quelques années...

https://codereview.stackexchange.com/questions/14542/multiple-inheritance-and-composition-with-java-and-c-updated

3
répondu series0ne 2017-04-13 12:40:36

pour réduire la complexité et simplifier le langage, l'héritage multiple n'est pas pris en charge en java.

envisager un scénario où A, B et c sont trois classes. La Classe C hérite des classes A et B. Si les classes A et B ont la même méthode et que vous l'appelez de l'objet de classe enfant, il y aura ambiguïté pour appeler la méthode de classe A ou B.

puisque les erreurs de temps de compilation sont meilleures que les erreurs d'exécution, java rend les erreurs de temps de compilation si vous héritez 2 classes. Ainsi, que vous ayez la même méthode ou une méthode différente, il y aura maintenant une erreur de compilation.

class A {  
    void msg() {
        System.out.println("From A");
    }  
}

class B {  
    void msg() {
        System.out.println("From B");
    }  
}

class C extends A,B { // suppose if this was possible
    public static void main(String[] args) {  
        C obj = new C();  
        obj.msg(); // which msg() method would be invoked?  
    }
} 
2
répondu Syeful Islam 2015-09-14 19:31:39

pour résoudre le problème de l'héritage mutiple en Java → l'interface est utilisée

J2EE (core JAVA) Notes By Mr. K. V. R Page 51

jour-27

  1. les Interfaces sont essentiellement utilisées pour développer des types de données définis par l'utilisateur.
  2. en ce qui concerne les interfaces, nous pouvons atteindre les objectifs suivants: concept d'héritages multiples.
  3. avec des interfaces nous pouvons réaliser le concept de polymorphisme, liaison dynamique et donc nous pouvons améliorer la performance d'un programme JAVA en tours de l'espace mémoire et de temps d'exécution.

une interface est une construction qui contient la collection de undefined méthodes ou d'une interface est une collection de purement abstrait méthode.

[...]

jour-28:

syntaxe-1 pour réutiliser les caractéristiques de l'interface (s) à la classe:

[abstract] class <clsname> implements <intf 1>,<intf 2>.........<intf n>
{
    variable declaration;
    method definition or declaration;
};

dans la syntaxe ci-dessus clsname représente le nom de la classe qui est hériter les fonctionnalités à partir du nombre " n " d'interfaces. ‘Implémente’ est un mot-clé qui est utilisée pour hériter les fonctionnalités de l'interface(s) à un la classe dérivée.

[...]

Syntaxe-2 hériter " n " le nombre d'interfaces à l'autre de l'interface:

interface <intf 0 name> extends <intf 1>,<intf 2>.........<intf n>
{     
    variable declaration cum initialization;
    method declaration;
};

[...]

syntaxe-3:

[abstract] class <derived class name> extends <base class name> implements <intf 1>,<intf 2>.........<intf n>
{
  variable declaration;
  method definition or declaration;
};
2
répondu Awesome 2015-11-02 15:15:36
  1. Définir interfaces pour définir les capacités. Vous pouvez définir plusieurs interfaces pour plusieurs capacités. Ces capacités peuvent être mises en œuvre par Animal ou oiseau .
  2. Utiliser héritage établir des relations entre les classes par le partage non-statique et de la non-public des données/méthodes.
  3. Utiliser Decorator_pattern pour ajouter des capacités de manière dynamique. Cela vous permettra de réduire le nombre de classes d'héritage et de combinaisons.

Regardez ci-dessous exemple pour une meilleure compréhension

quand utiliser le motif décorateur?

2
répondu Ravindra babu 2017-10-02 05:58:05