面试回答
因为如何要实现多继承,就会像C++中一样,存在菱形继承的问题,C++为了解决菱形继承问题,又引入了虚继承。因为支持多继承,引入了菱形继承问题,又因为要解决菱形继承问题,引入了虚继承。而经过分析,人们发现我们其实真正想要使用多继承的情况并不多。所以,在 Java 中,不允许“多继承”,即一个类不允许继承多个父类。
在 Java 8 以前,接口中是不能有方法的实现的。所以一类同时实现多个接口的时候,也不会出现 C++中的歧义问题。因为所有的方法都没有方法体,真正的实现还是在子类中的。但是,Java 8 中支持了默认函数(default method),即接口中可以定义一个有方法体的方法了。
而又因为 Java 支持同时实现多个接口,这就相当于通过 implements 就可以从多个接口中继承到多个方法了,但是,Java8 中为了避免菱形继承的问题,在实现的多个接口中如果有相同方法,就会要求该类必须重写这个方法。
知识扩展
菱形继承问题
Java 的创始人 James Cosling 曾经回答过,他表示:
“Java之所以不支持一个类继承多个类,主要是因为在设计之初我们听取了来自C++和Objective-C等阵营的人的意见。因为多继承会产生很多歧义问题。”
Gosling 老人家提到的歧义问题,其实是 C++ 因为支持多继承之后带来的菱形继承问题。
假设我们有类B和类C,它们都继承了相同的类 A。另外我们还有类D,类D通过多重继承机制继承了类B和类C。
这时候,因为D同时继承了B和C,并且B和C又同时继承了A,那么,D中就会因为多重继承,继承到两份来自A中的属性和方法。
这时候,在使用D的时候,如果想要调用一个定义在A中的方法时,就会出现歧义。
因为这样的继承关系的形状类似于菱形,因此这个问题被形象地成为菱形继承问题。
而C++为了解决菱形继承问题,又引入的虚继承。
因为支持多继承,引入了菱形继承问题,又因为要解决菱形继承问题,引入了虚继承,人们发现我们其实真正想要使用多继承的情况并不多。
所以,在Java中,不允许“声明多继承”,即一个类不允许继承多个父类。但是允许“实现多继承”,即一个类可以实现多个接口,一个接口也可以继承多个父接口。由于接口只允许有方法声明而不允许有方法实现(Java8 之前),这就避免了 C++ 中继承的歧义问题。
Java 8 中的多继承
Java 不支持多继承,但是是支持多实现的,也就是说,同一个类可以同时实现多个类。
我们知道,在 Java8以前,接口中是不能有方法的实现的。所以一个类同时实现多个接口的话,也不会出现 C++中的歧义问题。因为所有方法都没有方法体,真正的实现还是在子类中的。
那么问题来了。
Java 8 中支持了默认函数(default method),即接口中可以定义一个有方法体的方法了。
public interface Pet {public default void eat(){System.out.println("Pet Is Eating");}
}
而又因为Java支持同时实现多个接口,这就相当于通过 implements 就额可以从多个接口中继承到多个方法了,这不就是变相支持了多继承么。
那么,Java 是怎么解决菱形继承问题的呢?我们再定义一个哺乳动物接口,也定义一个eat方法。
public interface Mammal {public default void eat(){System.out.println("Mammal Is Eating");}
}
然后定义一个 Cat ,让他分别实现两个接口。
public class Cat implements Pet, Mammal {}
这时候,编译器会报错:
java: 类 com.chiyi.service.impl.Cat从类型 com.chiyi.service.Pet 和 com.chiyi.service.Mammal 中继承了eat() 的不相关默认值
这时候,就要求 Cat 类中,必须重写 eat() 方法。
public class Cat implements Pet, Mammal {@Overridepublic void eat() {System.out.println("Cat Is Eating");}
}
所以可以看到,Java 并没有帮我们解决多继承的歧义问题,而是把这个问题留给开发人员,通过重写方法的方式自己解决。