Java多重inheritance

为了充分理解如何解决Java的多重inheritance问题,我有一个经典的问题,我需要澄清。

可以说我有class级Animal这有分类BirdHorse ,我需要做一个class级PegasusBirdHorse延伸,因为Pegasus是一个鸟和一匹马。

我认为这是经典的钻石问题。 从我所能理解的经典方式来解决这个问题,就是让AnimalBirdHorse类从他们的界面实现Pegasus

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

你可以为动物类(生物意义上的类)创build接口,比如马的public interface Equidae和鸟类的public interface Avialae (我不是生物学家,所以这些术语可能是错的)。

那么你仍然可以创build一个

 public class Bird implements Avialae { } 

 public class Horse implements Equidae {} 

并且

 public class Pegasus implements Avialae, Equidae {} 

从评论中添加:

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

 public abstract class AbstractHorse implements Equidae {} public class Horse extends AbstractHorse {} public class Pegasus extends AbstractHorse implements Avialae {} 

更新

我想添加一个更多的细节。 正如Brian所说 ,这是OP已经知道的东西。

不过,我想强调的是,我build议绕过接口的“多inheritance”问题,我不build议使用代表已经是具体types的接口(比如Bird),而是更多的是一个行为鸭子打字,这也是好的,但我的意思只是:鸟类的生物类,Avialae)。 我也不build议使用以大写“I”开头的接口名称,例如IBird ,它只是告诉你为什么需要一个接口。 这就是问题的区别:使用接口构造inheritance层次结构,在有用时使用抽象类,在需要的地方实现具体的类并在适当的地方使用委托。

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

  • 首先是inheritance 。 正如你已经确定inheritance的限制意味着你不能在这里做你所需要的。
  • 第二个是构图 。 由于inheritance失败,你需要使用组合。

这个工作的方式是你有一个动物对象。 在那个对象中,你可以添加更多的对象来提供你需要的属性和行为。

例如:

  • 延伸动物实施IFlier
  • 延伸动物实施IHerbivore,IQuadruped
  • 飞马延伸动物实施IHerbivore,IQuadruped,IFlier

现在IFlier看起来像这样:

  interface IFlier { Flier getFlier(); } 

所以Bird看起来像这样:

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

现在你拥有inheritance的所有优点。 您可以重新使用代码。 你可以有一个IFlier的集合,并可以使用多态的所有其他优点等。

不过,你也可以从Composition中获得所有的灵活性。 您可以根据自己的喜好,为每种types的Animal应用许多不同的接口和复合支持类,并根据每个位的设置需要尽可能多的控制。

战略模式替代方法来组成

取决于你在做什么和如何做的另一种方法是让Animal基类包含一个内部集合来保存不同行为的列表。 在这种情况下,你最终会使用更接近战略模式的东西。 这在简化代码方面具有优势(例如, Horse不需要知道什么关于Quadruped HerbivoreHerbivore ),但是如果你也不使用接口方法,那么就失去了多态性的许多优点。

我有一个愚蠢的想法:

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

我可以提出鸭子打字的概念吗?

最有可能的是,你会倾向于使飞马扩展一个鸟和一个马界面,但鸭子打字实际上表明,你应该宁愿inheritance行为 。 正如评论中已经指出的那样,飞马不是一只鸟,但它可以飞翔。 所以你的Pegasus应该inheritance一个Flyable ,让我们说一个Gallopable

这种概念被用于战略模式 。 给出的例子实际上向你展示了鸭子是如何inheritanceFlyBehaviourQuackBehaviour ,还有鸭子,比如RubberDuck鸭子,它不能飞。 他们本来可以让Duck扩展一个Bird但是他们会放弃一些灵活性,因为每只Duck都能飞,甚至是可怜的RubberDuck Duck

从技术上来说,一次只能扩展一个类,实现多个接口,但是在软件工程的时候,我宁愿提出一个不一般的问题解决scheme。 顺便说一下,OO的做法是不错的, 不要扩展具体的类/只扩展抽象类来防止不必要的inheritance行为 – 不存在“动物”,也不能使用动物对象,而只能使用具体的动物。

把一匹马放在一个半门的马槽里是安全的,因为一匹马不能超过半门。 因此,我设置了马房屋服务,接受任何types的马的项目,把它放在一个半门稳定。

那么像马一样的马也能飞马?

我曾经想过很多关于多inheritance的问题,但是现在我已经编程了15年以上,不再关心实现多重inheritance了。

多数情况下,当我试图应付一个指向多重inheritance的devise时,我后来发现我错过了解问题领域。

要么

如果它看起来像鸭子,嘎嘎叫鸭子,但它需要电池,你可能有错误的抽象 。

在截至2014年2月仍处于开发阶段的Java 8中,您可以使用默认方法来实现一种类似C ++的多重inheritance。 你也可以看看这个教程 ,它显示了一些比官方文档更容易开始工作的例子。

Java没有多重inheritance问题,因为它没有多重inheritance。 这是为了解决真正的多重inheritance问题(钻石问题)而devise的。

有不同的策略来缓解这个问题。 最直接可实现的是Pavel提出的Composite对象(实质上C ++如何处理它)。 我不知道是否通过C3线性化(或类似)的多重inheritance是Java的未来的卡,但我怀疑它。

如果你的问题是学术性的,那么正确的解决scheme是鸟和马更具体,假设一个飞马只是一个鸟和一个马的组合是错误的。 说一个飞马具有与鸟和马共同的某些内在属性(也就是说他们也许有共同的祖先)是比较正确的。 莫里茨的答案指出,这可以被充分模拟。

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

如果你想要在你的飞马课堂里使用你的马和鸟的实现方法和function,那么你可以实现飞马作为一个鸟和一匹马的组成:

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

另一种可能性是使用实体组件系统方法而不是inheritance来定义动物。 当然,这意味着你将不会拥有这些动物的Java类,而只是由它们的组件来定义它们。

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

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

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

 public interface IAnimal { } public interface IBird implements IAnimal { } public interface IHorse implements IAnimal { } public interface IPegasus implements IBird,IHorse{ } 

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

 public class Bird implements IBird { } public class Horse implements IHorse{ } public class Pegasus implements IPegasus { } 

Ehm,你的类只能作为其他类的子类,但是你仍然可以使用尽可能多的接口。

飞马实际上是一匹能够飞翔的马(这是一匹马的特例)(这是这匹特殊马的“技能”)。 另一方面,你可以说,飞马是一只鸟,它可以走路,而且是四条腿 – 这一切都取决于你如何编写代码。

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

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

接口不模拟多重inheritance。 Java创build者认为多重inheritance是错误的,所以在Java中没有这样的东西。

如果你想把两个类的function组合成一个使用对象组合。 即

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

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

这里的接口可能会得心应手 – 如果Component1实现接口Interface1Component2实现Interface2 ,则可以定义

 class Main implements Interface1, Interface2 

这样你就可以在上下文允许的情况下交替使用对象。

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

正如您已经意识到的那样,Java中的类的多重inheritance是不可能的,但是接口是可能的。 你也可以考虑使用构图devise模式。

几年前我写了一篇非常全面的文章

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

为了减less复杂性并简化语言,Java中不支持多inheritance。

考虑A,B和C是三类的情况。 C类inheritance了A和B类。 如果A和B类具有相同的方法,并且您从子类对象调用它,那么调用A或B类的方法将会有歧义。

由于编译时错误比运行时错误更好,所以如果inheritance2个类,java会导致编译时错误。 所以不pipe你有相同的方法还是不同的,现在都会有编译时错误。

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

为解决Java中的多重inheritance问题→使用接口

J2EE(核心JAVA)笔记KVR先生Page 51

第二十七天

  1. 接口基本上用于开发用户定义的数据types。
  2. 关于接口,我们可以实现多重inheritance的概念。
  3. 通过接口,我们可以实现多态的概念,dynamic绑定,因此我们可以提高JAVA程序在内存空间和执行时间上的性能。

一个接口是一个包含纯粹未定义方法集合的构造,或者一个接口是纯粹抽象方法的集合。

[…]

第28天:

语法-1重用接口的function类:

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

在上述语法中,clsname表示从n个接口inheritance特征的类的名称。 “实现”是一个关键字,用于将接口的特性inheritance到派生类。

[…]

语法-2将“n”个接口inheritance到另一个接口:

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

[…]

语法-3:

 [abstract] class <derived class name> extends <base class name> implements <intf 1>,<intf 2>.........<intf n> { variable declaration; method definition or declaration; }; 
  1. 定义界面来定义function。 您可以为多个function定义多个接口。 这些function可以通过特定的动物来实现。
  2. 通过共享非静态和非公开的数据/方法,使用inheritance来build立类之间的关系。
  3. 使用Decorator_patterndynamic添加function。 这将允许您减lessinheritance类和组合的数量。

看看下面的例子,以便更好的理解

何时使用装饰模式?

Interesting Posts