Java Multiple Inheritance
为了全面了解如何解决Java的多重继承问题,我有一个经典的问题,我需要澄清。
假设我有
我认为这是典型的钻石问题。据我所知,解决这个问题的经典方法是使
我想知道是否还有其他的方法来解决这个问题,在这个问题中我仍然可以为鸟和马创建对象。如果有一种方法能够创造动物,那将是伟大的,但不是必要的。
您可以为动物类(生物学意义上的类)创建接口,例如马的
然后您仍然可以创建
1 2 | public class Bird implements Avialae { } |
和
1 | public class Horse implements Equidae {} |
而且
1 | public class Pegasus implements Avialae, Equidae {} |
从注释添加:
为了减少重复的代码,可以创建一个抽象类,该类包含要实现的动物的大多数公共代码。
1 2 3 4 5 | public abstract class AbstractHorse implements Equidae {} public class Horse extends AbstractHorse {} public class Pegasus extends AbstractHorse implements Avialae {} |
更新
我想再加一个细节。正如布赖恩所言,这是OP已经知道的。
然而,我要强调的是,我建议绕过接口的"多重继承"问题,我不建议使用已经表示具体类型(如bird)但更多是一种行为的接口(其他接口也指duck类型,这也很好,但我的意思只是:鸟类的生物类,Avialae)。我也不建议使用以大写字母"i"开头的接口名称,例如
将对象组合在一起有两种基本方法:
- 首先是继承。正如您已经确定的,继承的局限性意味着您不能在这里做您需要做的事情。
- 二是构图。由于继承失败,您需要使用组合。
它的工作方式是你有一个动物物体。然后在该对象中添加更多的对象,以提供所需的属性和行为。
例如:
- 鸟把动物的工具伸展得更快。
- 马伸展动物工具iherbivore,iquadrued
- 飞马座扩展了动物器具iHerbivore,iQuadruped,iLier
现在
1 2 3 | interface IFlier { Flier getFlier(); } |
所以
1 2 3 4 | class Bird extends Animal implements IFlier { Flier flier = new Flier(); public Flier getFlier() { return flier; } } |
现在你拥有了继承的所有优势。你可以重复使用代码。您可以有一个iflier集合,并且可以使用多态性等所有其他优点。
然而,您也有来自组合的所有灵活性。您可以将任意多个不同的接口和复合支持类应用到每种类型的
战略模式组合替代方法
根据您正在做什么和如何做,另一种方法是让
我有一个愚蠢的想法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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(); } } |
我可以提出鸭子打字的概念吗?
很可能你会让飞马座扩展一个鸟和马的接口,但duck类型实际上建议你应该继承行为。正如评论中所述,飞马座不是鸟,但它能飞。所以你的Pegasus应该继承一个
这种概念被用于战略模式中。给出的例子实际上向您展示了鸭子是如何继承
从技术上讲,一次只能扩展一个类并实现多个接口,但是在实践软件工程时,我更愿意建议一个特定于问题的解决方案,而不是通常可以回答的。顺便说一句,这是一个很好的OO实践,不是扩展具体的类/只是扩展抽象类以防止不必要的继承行为-没有"动物"这样的东西,也没有使用动物对象,只有具体的动物。
在Java 8中,它仍然在2014年2月的开发阶段,您可以使用默认方法来实现一种类似C++的多重继承。您还可以查看本教程,其中显示了一些比正式文档更容易开始使用的示例。
把马关在有半扇门的马厩里是安全的,因为马不能跨过半扇门。因此,我建立了一个马舍服务,接受任何类型的马,并把它放在一个马厩半门。
那么,像马一样的动物,即使是马也能飞吗?
我曾经考虑过很多关于多重继承的问题,但是现在我已经编程超过15年了,我不再关心实现多重继承。
通常,当我试图处理指向多个继承的设计时,我会在稍后发布我错过的理解问题域的设计。
或
If it looks like a duck and quacks like a duck but it needs
batteries, you probably have the wrong abstraction.
Java没有多重继承问题,因为它没有多重继承。这是通过设计,来解决真正的多重继承问题(钻石问题)。
有不同的缓解问题的策略。最快实现的是帕维尔提出的复合对象(基本上C++是如何处理的)。我不知道通过C3线性化(或类似的)是否有多重继承性是Java未来的关键,但我对此表示怀疑。
如果你的问题是学术性的,那么正确的解决方法是鸟和马更具体,假设飞马座仅仅是鸟和马的结合是错误的。更正确的说法是,飞马座具有某些与鸟类和马相同的内在属性(即它们可能有共同的祖先)。这可以充分模拟莫里茨的回答指出。
我认为这在很大程度上取决于你的需要,以及你的代码中如何使用动物类。
如果希望能够在Pegasus类中使用horse和bird实现的方法和特性,那么可以将Pegasus实现为bird和horse的组合:
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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | 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; } } } |
另一种可能性是使用实体组件系统方法而不是继承来定义动物。当然,这意味着,您将不会有单独的Java类动物,而是只由它们的组件定义。
实体组件系统方法的一些伪代码可能如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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); } |
您可以有一个接口层次结构,然后从选定的接口扩展类:
1 2 3 4 5 6 7 8 9 10 11 | public interface IAnimal { } public interface IBird implements IAnimal { } public interface IHorse implements IAnimal { } public interface IPegasus implements IBird,IHorse{ } |
然后根据需要定义类,通过扩展特定接口:
1 2 3 4 5 6 7 8 | public class Bird implements IBird { } public class Horse implements IHorse{ } public class Pegasus implements IPegasus { } |
ehm,您的类只能是另外一个类的子类,但是,您仍然可以实现任意多的接口。
飞马座实际上是一匹马(这是马的一种特殊情况),它能飞(这是这匹特殊马的"技能")。从另一方面来说,你可以说,飞马座是一只可以行走的鸟,有4条腿——这一切都取决于,你如何更容易编写代码。
就像你的情况一样,你可以说:
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 | 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 } } |
正如您已经意识到的,Java中的类的多重继承是不可能的,但是接口是可能的。您也可以考虑使用组合设计模式。
几年前我写了一篇非常全面的作文…
https://codereview.stackexchange.com/questions/14542/multiple-inheritance-and-composition-with-java-and-c-updated
请看下面的示例,以便更好地理解
什么时候使用装饰图案?
接口不模拟多重继承。Java创建者认为多重继承是错误的,所以Java中没有这样的东西。
如果要将两个类的功能组合成一个使用对象组合。即。
1 2 3 4 | public class Main { private Component1 component1 = new Component1(); private Component2 component2 = new Component2(); } |
如果要公开某些方法,请定义它们并让它们将调用委托给相应的控制器。
如果
1 | class Main implements Interface1, Interface2 |
以便在上下文允许的情况下可以互换使用对象。
所以在我看来,你不可能进入钻石问题。
为了解决Java和RARR中的多继承问题,使用了接口。
J2EE(核心Java)注释K.V.R Page先生51
Day - 27
Interfaces are basically used to develop user defined data types. With respect to interfaces we can achieve the concept of multiple inheritances. With interfaces we can achieve the concept of polymorphism, dynamic binding and hence we can improve the performance of a JAVA program in
turns of memory space and execution time.An interface is a construct which contains the collection of purely
undefined methods or an interface is a collection of purely abstract
methods.[...]
Day - 28:
Syntax-1 for reusing the features of interface(s) to class:
1
2
3
4
5 [abstract] class <clsname> implements <intf 1>,<intf 2>.........<intf n>
{
variable declaration;
method definition or declaration;
};In the above syntax clsname represents name of the class which is
inheriting the features from ‘n’ number of interfaces. ‘Implements’ is
a keyword which is used to inherit the features of interface(s) to a
derived class.[...]
Syntax-2 inheriting ‘n’ number of interfaces to another interface:
1
2
3
4
5 interface <intf 0 name> extends <intf 1>,<intf 2>.........<intf n>
{
variable declaration cum initialization;
method declaration;
};[...]
Syntax-3:
1
2
3
4
5 [abstract] class <derived class name> extends <base class name> implements <intf 1>,<intf 2>.........<intf n>
{
variable declaration;
method definition or declaration;
};
为了减少复杂性和简化语言,Java中不支持多重继承。
考虑一个场景,其中A、B和C是三个类。C类继承A和B类。如果A和B类有相同的方法,并且您从子类对象调用它,那么调用A或B类的方法会有歧义。
由于编译时错误优于运行时错误,如果继承2个类,Java会导致编译时错误。所以,无论您有相同的方法还是不同的方法,现在都会有编译时错误。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |