Java多重继承

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21824402/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 10:53:25  来源:igfitidea点击:

Java Multiple Inheritance

javaoopmultiple-inheritancediamond-problemmultiple-interface-implem

提问by Sheli

In an attempt to fully understand how to solve Java's multiple inheritance problems I have a classic question that I need clarified.

为了完全理解如何解决 Java 的多重继承问题,我有一个经典问题需要澄清。

Lets say I have class Animalthis has sub classes Birdand Horseand I need to make a class Pegasusthat extends from Birdand Horsesince Pegasusis both a bird and a horse.

可以说我有类Animal此有子类BirdHorse我需要做一个类Pegasus,从扩展BirdHorsePegasus既是一只鸟和一匹马。

I think this is the classic diamond problem. From what I can understand the classic way to solve this is to make the Animal, Birdand Horseclasses interfaces and implement Pegasusfrom them.

我认为这是经典的钻石问题。据我所知,解决这个问题的经典方法是制作Animal,BirdHorseclasses 接口并Pegasus从中实现。

I was wondering if there was another way to solve the problem in which I can still create objects for birds and horses. If there was a way to be able to create animals also that would be great but not necessary.

我想知道是否有另一种方法可以解决我仍然可以为鸟和马创建对象的问题。如果有一种方法也可以创造动物,那会很棒但不是必需的。

采纳答案by Moritz Petersen

You could create interfaces for animal classes (class in the biological meaning), such as public interface Equidaefor horses and public interface Avialaefor birds (I'm no biologist, so the terms may be wrong).

您可以为动物类(生物学意义上的类)创建接口,例如public interface Equidae为马和public interface Avialae鸟类(我不是生物学家,因此术语可能是错误的)。

Then you can still create a

然后你仍然可以创建一个

public class Bird implements Avialae {
}

and

public class Horse implements Equidae {}

and also

并且

public class Pegasus implements Avialae, Equidae {}


Adding from the comments:

从评论中补充:

In order to reduce duplicate code, you could create an abstract class that contains most of the common code of the animals you want to implement.

为了减少重复代码,您可以创建一个抽象类,其中包含您要实现的动物的大部分通用代码。

public abstract class AbstractHorse implements Equidae {}

public class Horse extends AbstractHorse {}

public class Pegasus extends AbstractHorse implements Avialae {}


Update

更新

I'd like to add one more detail. As Brian remarks, this is something the OP already knew.

我想再补充一个细节。正如布莱恩所说,这是 OP 已经知道的事情。

However, I want to emphasize, that I suggest to bypass the "multi-inheritance" problem with interfaces and that I don't recommend to use interfaces that represent already a concrete type (such as Bird) but more a behavior (others refer to duck-typing, which is good, too, but I mean just: the biological class of birds, Avialae). I also don't recommend to use interface names starting with a capital 'I', such as IBird, which just tells nothing about why you need an interface. That's the difference to the question: construct the inheritance hierarchy using interfaces, use abstract classes when useful, implement concrete classes where needed and use delegation if appropriate.

但是,我想强调的是,我建议绕过接口的“多继承”问题,并且我不建议使用表示已经是具体类型(例如 Bird)而是更多行为的接口(其他人参考鸭子打字,这也很好,但我的意思只是:鸟类的生物学类别,Avialae)。我也不建议使用以大写的“I”开头的接口名称,例如IBird,它只是没有说明您为什么需要接口。这就是问题的不同之处:使用接口构建继承层次结构,在有用时使用抽象类,在需要时实现具体类并在适当时使用委托。

回答by Smutje

Technically speaking, you can only extend one class at a time and implement multiple interfaces, but when laying hands on software engineering, I would rather suggest a problem specific solution not generally answerable. By the way, it is good OO practice, notto extend concrete classes/only extend abstract classes to prevent unwanted inheritance behavior - there is no such thing as an "animal" and no use of an animal object but only concrete animals.

从技术上讲,您一次只能扩展一个类并实现多个接口,但是在接触软件工程时,我更愿意建议一个通常无法回答的问题特定解决方案。顺便说一下,这是一个很好的面向对象实践,扩展具体类/只扩展抽象类以防止不需要的继承行为 - 没有“动物”这样的东西,也没有使用动物对象,而只有具体的动物。

回答by Karthik Surianarayanan

Interfaces don't simulate multiple inheritance. Java creators considered multiple inheritance wrong, so there is no such thing in Java.

接口不模拟多重继承。Java 创建者认为多重继承是错误的,所以 Java 中没有这种东西。

If you want to combine the functionality of two classes into one - use object composition. I.e.

如果您想将两个类的功能合二为一 - 使用对象组合。IE

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

And if you want to expose certain methods, define them and let them delegate the call to the corresponding controller.

如果您想公开某些方法,请定义它们并让它们将调用委托给相应的控制器。

Here interfaces may come handy - if Component1implements interface Interface1and Component2implements Interface2, you can define

这里接口可能会派上用场 - 如果Component1实现接口Interface1Component2实现Interface2,您可以定义

class Main implements Interface1, Interface2

So that you can use objects interchangeably where the context allows it.

这样您就可以在上下文允许的情况下互换使用对象。

So in my point of view, you can't get into diamond problem.

所以在我看来,你不能陷入钻石问题。

回答by András Iványi

Ehm, your class can be the subclass for only 1 other, but still, you can have as many interfaces implemented, as you wish.

嗯,您的类只能是其他 1 个的子类,但是,您仍然可以根据需要实现任意数量的接口。

A Pegasus is in fact a horse (it is a special case of a horse), which is able to fly (which is the "skill" of this special horse). From the other hand, you can say, the Pegasus is a bird, which can walk, and is 4legged - it all depends, how it is easier for you to write the code.

飞马其实就是一匹马(是马的特例),会飞(这就是这匹特马的“本领”)。另一方面,您可以说 Pegasus 是一只可以走路的鸟,并且是 4 条腿的——这完全取决于您如何更轻松地编写代码。

Like in your case you can say:

就像你的情况一样,你可以说:

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
   }
}

回答by richardtz

you can have an interface hierarchy and then extend your classes from selected interfaces :

您可以拥有一个接口层次结构,然后从选定的接口扩展您的类:

public interface IAnimal {
}

public interface IBird implements IAnimal {
}

public  interface IHorse implements IAnimal {
}

public interface IPegasus implements IBird,IHorse{
}

and then define your classes as needed, by extending a specific interface :

然后根据需要定义您的类,通过扩展特定接口:

public class Bird implements IBird {
}

public class Horse implements IHorse{
}

public class Pegasus implements IPegasus {
}

回答by Pavel Janicek

I have a stupid idea:

我有一个愚蠢的想法:

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();
  }
}

回答by snrlx

May I suggest the concept of Duck-typing?

我可以建议Duck-typing的概念吗?

Most likely you would tend to make the Pegasus extend a Bird and a Horse interface but duck typing actually suggests that you should rather inherit behaviour. As already stated in the comments, a pegasus is not a bird but it can fly. So your Pegasus should rather inherit a Flyable-interface and lets say a Gallopable-interface.

很可能您倾向于让 Pegasus 扩展 Bird 和 Horse 接口,但鸭子类型实际上表明您应该继承行为。正如评论中已经指出的那样,飞马不是鸟,但它可以飞。所以你的 Pegasus 应该继承一个Flyable-interface 并让我们说一个Gallopable-interface。

This kind of concept is utilized in the Strategy Pattern. The given example actually shows you how a duck inherits the FlyBehaviourand QuackBehaviourand still there can be ducks, e.g. the RubberDuck, which can't fly. They could have also made the Duckextend a Bird-class but then they would have given up some flexibility, because every Duckwould be able to fly, even the poor RubberDuck.

策略模式中使用了这种概念。在给定的例子其实显示了如何鸭子继承FlyBehaviourQuackBehaviour,仍然可以有鸭,例如RubberDuck,它不能飞。他们也可以将Duck扩展设置为Bird类,但随后他们会放弃一些灵活性,因为每个人Duck都可以飞行,即使是穷人RubberDuck

回答by Balder

I think it depends very much on your needs, and how your animal classes are to be used in your code.

我认为这在很大程度上取决于您的需求,以及如何在代码中使用动物类。

If you want to be able to make use of methods and features of your Horse and Bird implementations inside your Pegasus class, then you could implement Pegasus as a compositionof a Bird and a Horse:

如果您希望能够在 Pegasus 类中使用 Horse 和 Bird 实现的方法和功能,那么您可以将 Pegasus 实现为Bird 和 Horse的组合

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;
        }
    }
}

Another possibility is to use an Entity-Component-Systemapproach instead of inheritance for defining your animals. Of course this means, that you will not have individual Java classes of the animals, but instead they are only defined by their components.

另一种可能性是使用实体-组件-系统方法而不是继承来定义您的动物。当然,这意味着您将不会拥有动物的单独 Java 类,而是它们仅由它们的组件定义。

Some pseudo code for an Entity-Component-System approach could look like this:

实体-组件-系统方法的一些伪代码可能如下所示:

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);
}

回答by Tim B

There are two fundamental approaches to combining objects together:

将对象组合在一起有两种基本方法:

  • The first is Inheritance. As you have already identified the limitations of inheritance mean that you cannot do what you need here.
  • The second is Composition. Since inheritance has failed you need to use composition.
  • 第一个是继承。正如您已经确定继承的局限性意味着您不能在这里做您需要的事情。
  • 第二个是组合。由于继承失败,您需要使用组合。

The way this works is that you have an Animal object. Within that object you then add further objects that give the properties and behaviors that you require.

这样做的方式是你有一个 Animal 对象。然后在该对象中添加更多对象,以提供所需的属性和行为。

For example:

例如:

  • Birdextends Animalimplements IFlier
  • Horseextends Animalimplements IHerbivore, IQuadruped
  • Pegasusextends Animalimplements IHerbivore, IQuadruped, IFlier
  • Bird扩展了Animal工具IFlier
  • 扩展动物工具IHerbivore, IQuadruped
  • Pegasus扩展了Animal工具IHerbivore、IQuadruped、IFlier

Now IFlierjust looks like this:

现在IFlier看起来像这样:

 interface IFlier {
     Flier getFlier();
 }

So Birdlooks like this:

所以Bird看起来像这样:

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

Now you have all the advantages of Inheritance. You can re-use code. You can have a collection of IFliers, and can use all the other advantages of polymorphism, etc.

现在您拥有继承的所有优势。您可以重复使用代码。您可以拥有一组 IFlier,并且可以使用多态性等的所有其他优势。

However you also have all the flexibility from Composition. You can apply as many different interfaces and composite backing class as you like to each type of Animal- with as much control as you need over how each bit is set up.

但是,您也拥有 Composition 的所有灵活性。您可以将尽可能多的不同接口和复合支持类应用于每种类型Animal- 对每个位的设置方式进行尽可能多的控制。

Strategy Pattern alternative approach to composition

策略模式替代组合方法

An alternative approach depending on what and how you are doing is to have the Animalbase class contain an internal collection to keep the list of different behaviors. In that case you end up using something closer to the Strategy Pattern. That does give advantages in terms of simplifying the code (for example Horsedoesn't need to know anything about Quadrupedor Herbivore) but if you don't also do the interface approach you lose a lot of the advantages of polymorphism, etc.

根据您正在做什么和如何做的另一种方法是让Animal基类包含一个内部集合来保存不同行为的列表。在这种情况下,您最终会使用更接近策略模式的东西。这确实在简化代码方面提供了优势(例如Horse不需要知道任何关于Quadrupedor 的信息Herbivore),但是如果您不也使用接口方法,您将失去多态性等的许多优势。

回答by Mikkel L?kke

Java does not have a Multiple inheritance problem, since it does not have multiple inheritance. This is by design, in order to solve the real multiple inheritance problem (The diamond problem).

Java 没有多重继承问题,因为它没有多重继承。这是设计使然,以解决真正的多重继承问题(钻石问题)。

There are different strategies for mitigating the problem. The most immediately achievable one being the Composite object that Pavel suggests (essentially how C++ handles it). I don't know if multiple inheritence via C3 linearization (or similar) is on the cards for Java's future, but I doubt it.

有不同的策略可以缓解这个问题。最直接可实现的对象是 Pavel 建议的 Composite 对象(本质上是 C++ 处理它的方式)。我不知道 Java 的未来是否会通过 C3 线性化(或类似的)进行多重继承,但我对此表示怀疑。

If your question is academic, then the correct solution is that Bird and Horse are more concrete, and it is false to assume that a Pegasus is simply a Bird and a Horse combined. It would be more correct to say that a Pegasus has certain intrinsic properties in common with Birds and Horses (that is they have maybe common ancestors). This can be sufficiently modeled as Moritz' answer points out.

如果你的问题是学术性的,那么正确的答案是鸟和马更具体,假设飞马只是鸟和马的组合是错误的。更准确地说,飞马座与鸟类和马匹具有某些共同的内在属性(即它们可能有共同的祖先)。正如 Moritz 的回答所指出的那样,这可以充分建模。