要实现多重继承,我们必须使用接口,但是为什么接口方法没有主体,为什么必须在派生类中重写它们呢?
我真的想要一个清晰的答案,不涉及太多的计算机术语,我似乎无法理解这一点,我参考了各种参考资料
要实现多重继承,我们必须使用接口,但是为什么接口方法没有主体,为什么必须在派生类中重写它们呢?
我真的想要一个清晰的答案,不涉及太多的计算机术语,我似乎无法理解这一点,我参考了各种参考资料
因为 Java 与 C++ 或 Eiffel 等语言相比,只有类型的多重继承(即接口和一个类),而没有状态和行为的多重继承。后者增加了巨大的复杂性(尤其是状态)。
Java 设计者(以及 C#)选择不包含它,因为它经常给 C++ 程序员带来非常难以调试的问题。您可以通过实现多个接口来解决几乎大多数需要真正多重继承的问题,因此这种权衡被认为是值得的。
请注意,行为的多重继承(不是状态)可能会以虚拟扩展方法的形式出现在 Java 8 中(除非它们像许多其他事情之一一样再次推迟它),其中接口可以声明一个委托给另一个类中的方法的方法,然后它存在于实现该接口的所有类型上。
接口声明实现类提供什么服务,而不是如何(这是实现类的工作)。多重继承被认为是不好的,因为它会导致复杂的代码和类层次结构。
接口只有常量变量(public + static + final)和抽象方法(public & abstract)。这些是由实现接口的类使用的。
接口只是说“我是一个契约”,如果你想使用它,应该遵守一些规则(为所有抽象方法提供实现)。
Java 中省略了多重继承,确保一个类只能扩展 1 个类,以避免菱形问题。无论如何,您都可以通过使用接口在 Java 中实现类型的多重继承。
Java 接口包含必须由实现该接口的类实现的方法列表。因此,方法没有主体:每个方法的主体都在实现类中。
简单的答案:
接口提供了实现的标准。
解释:
在 Java 中,接口类似于抽象类,因为它的成员没有实现。例如,
public interface Comparable
{ boolean less(Object m);
boolean greater(Object m);
boolean lessEqual(Object m);
boolean greaterEqual(Object m);
}
接口提供了实现的标准。
使用接口的好处是它们可以模拟多重继承。Java 中的所有类都必须只有一个基类,唯一的例外是java.lang.Object
(Java 类型系统的根类);java中不允许类的多重继承。
所有实例方法都是隐式的public
和abstract
。您可以这样标记它们,但不鼓励这样做,因为标记被认为是过时的做法。接口本身不需要是公共的,标准库中的几个接口不是公共的,因此只能在内部使用。
接口创建类可以实现的协议。请注意,可以像扩展类一样扩展接口(以获取新接口) 。一个实际上可以扩展几个接口。接口因此享有多重继承的好处。(类没有。)接口的多重继承几乎没有缺点(小名称冲突问题是一个例外)。实现的多重继承有很大的缺点C++
,如. 这些包括效率考虑因素以及确定在某些情况下将执行哪些代码的语义难度。
实现Comparable的多项式类需要实现接口中声明的所有函数。
public class Polynomial implements Comparable
{ . . .
boolean less(Object m){ . . . }
boolean greater(Object m){ . . . }
boolean lessEqual(Object m){ . . . }
boolean greaterEqual(Object m){ . . . }
Polynomial multiply(Polynomial P){ . . . }
. . .
}
一个类可以选择实现任意数量的接口。实现接口的类必须为该接口的所有方法提供主体。此外,我们希望抽象类可以选择实现接口的一部分,而将其余部分留给非抽象子类。
接口的用处远不止为其他程序员发布协议。任何函数都可以具有接口类型的参数。实现接口的类中的任何对象都可以作为参数传递。
接口方法没有像
public interface Flyable
{
public void fly();
}
因为接口本身不会做任何事情
接口是定义合约。
另一方面,接口定义了一个类可以做什么,而不是它是什么。所以界面通常是关于动词的。
所以 Flyable 接口除了定义植入的 FlyableObjects 将要飞行的合约之外什么都不做。
像:
class Rocket implements Flyable
{
public void fly()
{
// do the stuffs.
}
}
当然,我们也只能通过接口实现多重继承。
如果接口有实体,那么它就会带回致命的死亡戴蒙德问题。
考虑这个具有与主体接口的示例
interface A {
void print(){ System.out.print("A") }
}
interface B {
void print(){ System.out.print("B") }
}
interface C extends A, B {
// now since A and B have bodies interfaces would have had choice to not to override the default behavior
}
public class C_Implementer implements C{
public static void main(String args[]){
C c = new C_Implementer();
c.print(); // gotcha!!!!! what should it print, A or B????
}
}
您在问“为什么 Java 不支持实现的多重继承?”
这在 Java 教程,状态、实现和类型的多重继承中进行了讨论,但我想给出一个具体的例子来说明实现的多重继承问题(以及最后的新语言特性解决方案)。
想象一下定义同名方法的两个接口(在我们提出的允许接口方法主体的 Java 版本中)。
public interface FaceOne {
public void method() {
System.out.println("FaceOne Version");
}
}
public interface FaceTwo {
public void method() {
System.out.println("FaceTwo Version");
}
}
一个类实现了这两个接口,但不覆盖该方法。
public class Inheriter implements FaceOne, FaceTwo {
}
当我调用Inheriter.method()
时,由于类继承了其祖先的方法,因此出现了问题:输出打印“FaceOne Version”还是“FaceTwo Version”?
此外,如果该类要覆盖该方法,但还想使用 调用其祖先的版本super
,编译器将再次在该方法的版本之间进行选择。
这就是Java不支持实现的多重继承的原因。
顺便说一句,我认为将其实现到语言中的一种优雅方法如下:
继续强制实现类覆盖其祖先接口的方法。这解决了非覆盖方法的第一个问题。
然后,使用与访问内部类的封闭实例类似的符号来访问特定的祖先接口super
。Inheriter 类将有多个选项:
不要调用super.method()
,而是只使用新定义的实现。
用于FaceOne.super.method()
使默认继承的实现输出“FaceOne 版本”。
用于FaceTwo.super.method()
使默认继承的实现输出“FaceTwo 版本”。
使用上述组合:
一种实现可能是:
@Override
public void method() {
FaceOne.super.method();
FaceTwo.super.method();
System.out.println("Inheriter Version");
}
输出:
FaceOne 版本
FaceTwo 版本
继承者版本
编辑:根据这个问题,这显然正是 Java 8 中默认实现的结构。