为了完全理解如何解决Java的多重继承问题,我有一个经典的问题需要澄清 .
让我说我有类 Animal
这有子类 Bird
和 Horse
我需要创建一个从 Bird
和 Horse
延伸的类 Pegasus
,因为 Pegasus
既是鸟又是马 .
我认为这是经典的钻石问题 . 根据我的理解,解决这个问题的经典方法是制作 Animal
, Bird
和 Horse
类接口并从中实现 Pegasus
.
我想知道是否有另一种方法来解决我仍然可以为鸟类和马创造物体的问题 . 如果有一种方法可以创造动物,那将是伟大的但不是必要的 .
16 回答
你可以为动物类(生物学意义上的类)创建接口,例如马的
public interface Equidae
和鸟的public interface Avialae
(我不是生物学家,所以这些术语可能是错的) .然后你仍然可以创建一个
和
并且
从评论中添加:
为了减少重复代码,您可以创建一个抽象类,其中包含您要实现的动物的大多数常用代码 .
更新
我想补充一点细节 . 作为Brian remarks,这是OP已经知道的事情 .
但是,我想强调一点,我建议绕过接口的"multi-inheritance"问题,并且我不建议使用以大写'I'开头的接口名称,例如
IBird
,它只是告诉你为什么需要一个接口 . 这就是问题的不同之处:使用接口构造继承层次结构,在有用时使用抽象类,在需要时实现具体类,并在适当时使用委托 .将对象组合在一起有两种基本方法:
第一个是 Inheritance . 正如您已经确定继承的局限性意味着您无法在此处执行所需操作 .
第二个是 Composition . 由于继承失败,您需要使用组合 .
这种方式的工作方式是你有一个Animal对象 . 然后在该对象中添加进一步的对象,这些对象提供您需要的属性和行为 .
例如:
Bird extends Animal implements IFlier
Horse extends Animal implements IHerbivore, IQuadruped
Pegasus extends Animal implements IHerbivore, IQuadruped, IFlier
现在
IFlier
看起来像这样:所以
Bird
看起来像这样:现在你拥有继承的所有优点 . 您可以重复使用代码 . 您可以拥有一组IFliers,并可以使用多态性等所有其他优点 .
但是,您也拥有Composition的所有灵活性 . 您可以根据需要为每种类型的
Animal
应用尽可能多的不同接口和复合支持类 - 您需要对每个位的设置进行尽可能多的控制 .Strategy Pattern alternative approach to composition
根据您正在做什么以及如何做的另一种方法是让
Animal
基类包含一个内部集合来保存不同行为的列表 . 在这种情况下,您最终会使用更接近战略模式的东西 . 这确实在简化代码方面具有优势(例如Horse
不需要知道关于Quadruped
或Herbivore
的任何内容),但如果你不进行接口方法,则会失去多态性等许多优点 .我有一个愚蠢的想法:
我可以建议Duck-typing的概念吗?
很可能你倾向于让Pegasus延伸一个Bird and a Horse界面,但鸭子打字实际上暗示你应该宁愿继承行为 . 正如评论中已经说明的那样,飞马座不是一只鸟,但它可以飞翔 . 所以你的Pegasus应该继承
Flyable
-interface并让我们说一个Gallopable
-interface .Strategy Pattern中使用了这种概念 . 给出的示例实际上向您展示了鸭子如何继承
FlyBehaviour
和QuackBehaviour
并且仍然可以有鸭子,例如RubberDuck
,不能飞 . 他们本可以使Duck
延伸Bird
-class然后他们会放弃一些灵活性,因为每个Duck
都能够飞,甚至是穷人RubberDuck
.从技术上讲,您一次只能扩展一个类并实现多个类接口,但在进行软件工程时,我宁愿建议一个问题特定的解决方案通常不负责任 . 顺便说一句,这是一个很好的OO实践,不扩展具体类/只扩展抽象类来防止不必要的继承行为 - 没有"animal"这样的东西,也没有使用动物对象而只使用具体的动物 .
将马放在有半门的马厩里是安全的,因为马不能越过半门 . 因此,我设置了马匹服务,接受任何类型的马类,并把它放在一个半门的马厩里 .
那种像马一样的动物甚至可以飞马吗?
我过去常常考虑多重继承,但是现在我已经编程了超过15年,我不再关心实现多重继承了 .
通常情况下,当我试图应对一个指向多重继承的设计时,我后来发布了我错过了解问题域 .
要么
在Java 8中,自2014年2月起仍处于开发阶段,您可以使用default methods来实现一种类似C的多重继承 . 您还可以查看this tutorial,其中显示了一些比官方文档更容易开始使用的示例 .
Java没有多重继承问题,因为它没有多重继承 . 这是设计,以解决真正的多重继承问题(钻石问题) .
有不同的策略可以缓解这个问题 . 最容易实现的是Pavel建议的Composite对象(基本上是C如何处理它) . 我不知道通过C3线性化(或类似)的多重继承是否适用于Java的未来,但我对此表示怀疑 .
如果你的问题是学术性的,那么正确的解决方案是鸟和马更具体,假设飞马只是一只鸟和马的组合是错误的 . 说Pegasus具有与鸟类和马匹相同的某些内在特性(即它们可能具有共同的祖先)会更为正确 . Moritz的答案指出,这可以充分模仿 .
我认为这在很大程度上取决于您的需求,以及您的动物类如何在您的代码中使用 .
如果您希望能够在Pegasus课程中使用您的马和鸟实施的方法和功能,那么您可以将Pegasus实施为鸟和马的composition:
另一种可能性是使用Entity-Component-System方法而不是继承来定义您的动物 . 当然,这意味着您不会拥有动物的单独Java类,而是仅由它们的组件定义 .
实体 - 组件 - 系统方法的一些伪代码可能如下所示:
您可以拥有一个接口层次结构,然后从选定的接口扩展您的类:
然后通过扩展特定的接口来根据需要定义类:
嗯,你的类可以只是另外一个的子类,但是,你可以根据需要实现尽可能多的接口 .
Pegasus实际上是一匹马(它是一匹特殊的马),能够飞行(这是这匹特殊马的“技能”) . 另一方面,你可以说,Pegasus是一只可以行走的鸟,并且是四腿的 - 这完全取决于你如何更容易编写代码 .
就像你的情况一样,你可以说:
接口不模拟多重继承 . Java创建者认为多重继承是错误的,因此在Java中没有这样的东西 .
如果要将两个类的功能合并为一个 - 使用对象组合 . 即
如果要公开某些方法,请定义它们并让它们将调用委托给相应的控制器 .
这里的接口可能会派上用场 - 如果
Component1
实现接口Interface1
和Component2
实现Interface2
,你可以定义这样您就可以在上下文允许的情况下交替使用对象 .
所以在我看来,你无法进入钻石问题 .
正如您已经意识到的那样,Java中的类的多重继承是不可能的,但是可以使用接口 . 您可能还想考虑使用组成设计模式 .
几年前我写了一篇关于作文的非常全面的文章......
https://codereview.stackexchange.com/questions/14542/multiple-inheritance-and-composition-with-java-and-c-updated
为了降低复杂性并简化语言,java中不支持多重继承 .
考虑A,B和C是三个类的场景 . C类继承A和B类 . 如果A和B类具有相同的方法并且您从子类对象调用它,则调用A或B类的方法会有歧义 .
由于编译时错误优于运行时错误,因此如果继承2个类,则java会呈现编译时错误 . 因此,无论您使用相同的方法还是不同的方法,现在都会出现编译时错误 .
为了解决Java中多重继承的问题→使用了接口
J2EE (core JAVA) Notes By Mr. K.V.R Page 51
定义 interfaces 以定义功能 . 您可以为多个功能定义多个接口 . 这些功能可以通过特定的 Animal 或 Bird 实现 .
使用 inheritance 通过共享非静态和非公共数据/方法在类之间 Build 关系 .
使用Decorator_pattern动态添加功能 . 这将允许您减少继承类和组合的数量 .
请看下面的示例以便更好地理解
When to Use the Decorator Pattern?