我知道Java和C#中不允许多重继承。许多书只是说,不允许多重继承。但是可以通过使用接口来实现。没有任何关于为什么不允许它的讨论。谁能确切告诉我为什么不允许这样做?
我知道Java和C#中不允许多重继承。许多书只是说,不允许多重继承。但是可以通过使用接口来实现。没有任何关于为什么不允许它的讨论。谁能确切告诉我为什么不允许这样做?
Answers:
简短的答案是:因为语言设计师决定不这样做。
基本上,.NET和Java设计器似乎都不允许多重继承,因为他们认为添加MI会增加语言的复杂性,而带来的好处却很少。
要获得更有趣和深入的阅读,Web上提供了一些文章,并对一些语言设计师进行了采访。例如,对于.NET,克里斯·布鲁姆(Chris Brumme)(曾在CLR的MS上工作)解释了他们决定不这样做的原因:
实际上,不同的语言对MI的工作方式有不同的期望。例如,如何解决冲突以及重复的碱基是合并的还是冗余的。在CLR中实现MI之前,我们必须对所有语言进行调查,弄清楚常见的概念,并决定如何以与语言无关的方式来表达它们。我们还必须确定MI是否属于CLS,以及对于不希望使用此概念的语言(例如,大概是VB.NET)意味着什么。当然,这是我们作为公共语言运行时所从事的业务,但是我们还没有为MI做这件事。
实际上,真正适合使用MI的地方数量很少。在许多情况下,多接口继承可以代替工作。在其他情况下,您可能可以使用封装和委派。如果我们要添加一个稍微不同的结构(如mixin),实际上会更强大吗?
多重实现继承为实现注入了很多复杂性。这种复杂性会影响投射,布局,调度,现场访问,序列化,身份比较,可验证性,反射,泛型以及可能还有很多其他地方。
对于Java,您可以阅读本文:
从Java语言中省略多个继承的原因主要来自“简单,面向对象和熟悉的”目标。作为一种简单的语言,Java的创建者想要一种大多数开发人员无需大量培训即可掌握的语言。为此,他们努力使该语言尽可能类似于C ++(熟悉),而又不承担C ++不必要的复杂性(简单)。
在设计人员看来,多重继承会带来更多无法解决的问题和混乱。因此,他们削减了语言的多重继承(就像削减了运算符的重载一样)。设计师丰富的C ++经验告诉他们,多重继承根本不值得头疼。
实现的多重继承是不允许的。
问题是,如果您有一个Cowboy和Artist类(都具有draw()方法的实现),然后尝试创建新的CowboyArtist类型,则编译器/运行时无法弄清楚该怎么办。调用draw()方法会怎样?有人死在街上吗,还是您有可爱的水彩画?
我相信这就是双重钻石继承问题。
原因: Java非常简单,因此非常流行并且易于编码。
因此,对于Java开发人员来说,对于程序员来说,理解起来既困难又复杂,他们都试图避免这种情况。这种属性之一就是多重继承。
多继承问题:钻石问题。
范例:
这是钻石问题中存在的歧义。
解决这个问题并非不可能,但是在阅读时会给程序员带来更多的困惑和复杂性。 它引起的问题比它试图解决的更多。
注意:但是,无论如何,您始终可以通过使用接口间接实现多重继承。
因为Java具有与C ++截然不同的设计理念。(我不在这里讨论C#。)
在设计C ++时,Stroustrup希望包括有用的功能,无论它们如何被滥用。可以通过多重继承,运算符重载,模板和其他各种功能来浪费大量时间,但是也可以使用它们来做一些非常好的事情。
Java设计哲学是强调语言构造中的安全性。结果是有些事情要做起来很尴尬,但是您可以更加确信所查看的代码意味着您所认为的是正确的。
而且,Java在很大程度上是最著名的OO语言C ++和Smalltalk的反应。还有许多其他的OO语言(Common Lisp实际上是第一个被标准化的语言),而不同的OO系统可以更好地处理MI。
更不用说完全有可能使用接口,组合和委派在Java中进行MI。它比C ++更明确,因此使用起来比较笨拙,但乍一看会为您带来一些您更可能理解的知识。
这里没有正确的答案。有不同的答案,在给定情况下哪种更好取决于应用程序和个人偏好。
多重继承为
因此,将多重继承不包括在Java语言中是明智的选择。
早在上世纪70年代,计算机科学成为一门科学,而批量生产却少了,程序员有时间去考虑良好的设计和良好的实现,因此产品(程序)的质量很高(例如TCP / IP设计)。和实施)。如今,当每个人都在编程时,管理人员在截止日期之前更改规范时,很难跟踪到像Steve Haigh帖子中的Wikipedia链接中所述的那些细微问题。因此,“多重继承”受到编译器设计的限制。如果喜欢,您仍然可以使用C ++ ....,并拥有所有想要的自由:)
我发表了这样的声明:“在Java中不允许多重继承”。
当“类型”从多个“类型”继承时,定义了多重继承。并且接口由于具有行为也被归类为类型。因此Java确实具有多重继承。只是更安全。
Java具有概念,即多态。Java中有2种类型的多态性。有方法重载和方法重载。其中,方法重写发生在父类和子类之间。如果我们要创建一个子类的对象并调用超类的方法,并且如果子类扩展了多个类,则应调用哪种超类方法?
或者,在通过调用超类构造函数时super()
,将调用哪个超类构造函数?
当前的Java API功能无法做出此决定。因此在Java中不允许多重继承。
直接在Java中不允许多重继承,但通过接口是允许的。
原因:
多重继承:引入更多的复杂性和歧义性。
接口:接口是Java中的完全抽象类,为您提供了一种统一的方法,可以从其公共可用接口正确地描述程序的结构或内部工作,从而带来更大的灵活性,可重用的代码以及更多的控制有关如何创建其他类并与之交互的信息。
更准确地说,它们是Java中的特殊构造,具有附加的特征,使您可以执行多种继承,即可以上载到多个类的类。
让我们举一个简单的例子。
假设有两个具有相同方法名称但功能不同的超类类A和B。通过以下带有(extends)关键字的代码,多重继承是不可能的。
public class A
{
void display()
{
System.out.println("Hello 'A' ");
}
}
public class B
{
void display()
{
System.out.println("Hello 'B' ");
}
}
public class C extends A, B // which is not possible in java
{
public static void main(String args[])
{
C object = new C();
object.display(); // Here there is confusion,which display() to call, method from A class or B class
}
}
但是通过接口,使用(实现)关键字可以实现多重继承。
interface A
{
// display()
}
interface B
{
//display()
}
class C implements A,B
{
//main()
C object = new C();
(A)object.display(); // call A's display
(B)object.display(); //call B's display
}
}
在C ++中,一个类可以(直接或间接地)从多个类继承(这称为 多重继承)。
但是,C#和Java将类限制为单一继承,每个类都从单个父类继承。
多重继承是创建将两个不同的类层次结构的各个方面组合在一起的类的有用方法,当在单个应用程序中使用不同的类框架时,经常会发生这种情况。
例如,如果两个框架为异常定义了自己的基类,则可以使用多重继承来创建可与任一框架一起使用的异常类。
多重继承的问题在于它可能导致歧义。一个典型的例子是,一个类从另外两个类继承,而每个其他类又从同一个类继承:
class A {
protected:
bool flag;
};
class B : public A {};
class C : public A {};
class D : public B, public C {
public:
void setFlag( bool nflag ){
flag = nflag; // ambiguous
}
};
在此示例中,flag
数据成员由定义class A
。但是class D
从下降class B
和class C
,这两个派生的A
,所以在本质上两个副本的flag
可用,因为两个实例A
都在D
的类层次结构。您要设定哪一个?编译器会抱怨引用flag
的D
是模棱两可的。一种解决方法是明确消除引用的歧义:
B::flag = nflag;
另一个解决方法是将B和C声明为virtual base classes
,这意味着层次结构中只能存在A的一个副本,从而消除了任何歧义。
多重继承还存在其他复杂性,例如,在构造派生对象时初始化基类的顺序,或者成员无意中对派生类隐藏的方式。为了避免这些复杂性,某些语言将其自身限制为更简单的单一继承模型。
尽管这确实大大简化了继承,但是由于只有具有共同祖先的类才能共享行为,因此它也限制了它的用途。接口通过允许不同层次结构中的类公开通用接口,即使它们不是通过共享代码实现的,也从某种程度上减轻了此限制。
想象一下这个例子:我有一堂课 Shape1
它具有CalcualteArea
方法:
Class Shape1
{
public void CalculateArea()
{
//
}
}
还有另一类Shape2
也有相同的方法
Class Shape2
{
public void CalculateArea()
{
}
}
现在我有一个子类Circle,它从Shape1和Shape2派生而来;
public class Circle: Shape1, Shape2
{
}
现在,当我为Circle创建对象并调用该方法时,系统不知道要调用哪个计算区域方法。两者具有相同的签名。因此编译器会造成混乱。这就是为什么不允许多重继承。
但是可以有多个接口,因为接口没有方法定义。即使两个接口都具有相同的方法,两个接口都没有任何实现,并且始终将执行子类中的方法。