Table of Contents

Java8 接口多继承

前言

首先,我们可以明确两点:

  1. 作为java程序员都熟知Java是一种面向对象的只允许单继承的语言,避免了C++支持多继承带来的菱形继承问题。

  2. Java的接口是可以继承多个接口的。

问题

为何接口可以多继承extends接口呢?会不会出现类似菱形继承的问题?

其实是不会的,由于接口只是定义某个行为的名称,即使继承的多个接口中出现了同名的方法名,实现类中也有且只会有一个实现,所以并不会出现结构混乱的情况。

当然这里所指的只定义方法,不实现方法的接口是出现在Java 8之前,到了Java8就不一样了,因为在接口中新增了默认方法。

默认方法示例

默认方法是接口中用default修饰的方法,其中包含实现的内容。比如下面的例子:

1
2
3
4
5
6
7
8
public interface InterfaceDemo {
    // 普通方法,只定义,不实现
    void oldFun();
    // 默认方法,又定义,又实现
    default void newFun(){
        System.out.println("newFun");
    }
}

为什么出现默认方法

之前的接口好处是面向抽象而不是面向具体编程,缺陷是,当需要修改接口时候,需要修改全部实现该接口的类。

由于系统升级难免会带来新功能的加入,这种情形较多,然而修改起来麻烦,容易出错。比如java 8之前的集合框架没有foreach方法,通常能想到的解决办法是在JDK里给相关的接口添加新的方法及实现。然而,对于已经发布的版本,是没法在给接口添加新方法的同时不影响已有的实现。

所以引进的默认方法。他们的目的是为了解决接口的修改与现有的实现不兼容的问题。使得接口升级变得更加平滑。

Java8现在会不会出现“菱形继承”

可以说Java现在也存在了“多重继承”,实质上是Java8的接口多继承,与实现多继承不同。但是也会涉及实现多继承的问题。

面临的一个新问题,就是多重继承带来的二义性问题,有点类似之前介绍的致命方块(也叫菱形问题)

如下面的UML图所示

上面这种,无法知道A会调用哪个接口的默认fun方法,所以编译器会报错。符合要求,不存在结构混乱的问题。

解决该类问题的方法:

  • 先覆写fun方法
  • 再显示声明调用哪个接口的默认fun方法
 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
public class A implements B,C{

    @Override
    public void fun(){
        // 显示调用B的默认方法
        B.super.fun();
    }

    public static void main(String[] args) {
        A a = new A();
        // 这里会打印B的fun
        a.fun();
    }
}
interface D{
    default void fun(){
        System.out.println("D");
    }
}
interface B extends D{
    @Override
    default void fun(){
        System.out.println("B");
    }
}
interface C extends D{
    @Override
    default void fun(){
        System.out.println("C");
    }
}

java对默认方法的冲突判断有一定的规则:

  • 多重继承的冲突

    • 一个声明在类里面的方法优先于任何默认方法(classes always win)

    • 否则,则会优先选取路径最短的。

    关于多重继承冲突更多的代码示例可以参考Java8 默认方法

参考资料

解决Java8接口default方法多继承冲突问题

Java8中的默认方法

Java8 默认方法