一文学会Java 8 菱形继承问题

在Java 8 中,接口可以使用default关键字添加方法的默认实现,主要目的是为了解决依赖升级引起不兼容的问题。

当接口中某个新增的方法子类中没有去实现时,就会走接口中的默认实现。

而Java一直是单继承,Java 8 之前不会出现类似C++中的多继承引发的菱形继承问题。

Java 8 接口的默认实现同时也带来菱形继承问题。

假设GrandFather接口如下:

public interface GrandFather {

    default void say(){
        System.out.println("hello,I'm grandfather");
    }
}

Father1Father2分别继承了GrandFather接口,并且重写了其say()方法

public interface Father1 extends GrandFather{

    @Override
    default void say(){
        System.out.println("hello,I'm Father1");
    }
}
public interface Father2 extends GrandFather{
    @Override
    default void say(){
        System.out.println("hello,I'm Father2");
    }
}

新加一个Son子类,同时实现Father1Father2接口

public class Son implements Father1,Father2{

}

当我们再main()方法中去创建Son对象,调用say()方法

public static void main(String[] args) {
  GrandFather son = new Son();
  son.say();
}

image-20220104152325761

输出结果(报错):

/Users/test/workspace/idea/demo/src/main/java/com/demo/Son.java:3:8
java: 类 com.demo.Son从类型 com.demo.Father1 和 com.demo.Father2 中继承了say() 的不相关默认值

根据输出结果可知,编译系统帮我们拦截住了错误,因为我们调用Son对象的say()方法,是无法判断具体走哪一个接口中的say()方法的默认实现的。

那么,这个判断标准是什么

  • 类方法优先级最高。类或父类中声明的方法的优先级高于任何声明为默认方法的优先级。
  • 函数签名相同时,优先选择拥有最具体实现的默认方法的接口。
  • 继承多个接口的类必须通过显示覆盖和调用期望方法,显示指定使用哪一个方法的实现。

以上内容引用自人民邮电出版社的Java 8实现(Java 8 in Action)

标准一:类方法优先级最高。类或父类中声明的方法的优先级高于任何声明为默认方法的优先级。

这个理解起来较为简单,如果我们在Son类中重写了say()方法,那么该方法优先级最高!

继承关系不变,Son类改为如下:

public class Son implements Father1,Father2{
    @Override
    public void say() {
        System.out.println("hello,I'm Son");
    }
}

执行结果如下:

hello,I'm Son

Process finished with exit code 0

标准二:函数签名相同时,优先选择拥有最具体实现的默认方法的接口

Son改为原来的实现,移除对Father2实现关系,Son类如下:

public class Son implements Father1{
 
}

image-20220104155302595

当我们调用Son对象中的say()方法时,只会找到Father1GrandFather中的say()方法

Father1中的say()方法又是重写GrandFather中的say()方法

对于Son对象而言,Father1中的say()方法更加具体,因此执行时必定调用的是Father1中的say()方法的默认实现

执行结果如下:

hello,I'm Father1

Process finished with exit code 0

标准三:继承多个接口的类必须通过显示覆盖和调用期望方法,显示指定使用哪一个方法的实现

回到初始的状态,Son类实现Father1Father2,

对于Son对象而言,Father1中的say()方法和Father2中的say()方法优先级是一致的,因此我们必须显示指定期望调用的方法

public class Son implements Father1,Father2{

    @Override
    public void say() {
        Father2.super.say();
    }
}

指定方式格式:类名+super.+方法名

image-20220104152325761

这种方式可以理解为标准一的变式

由于我们Son中重写了say()方法,根据标准一,调用的时候必定会调用Son中的方法

而在Son中的say()方法中显示指定调用具体接口的具体默认实现

补充:

如果我们使用的IDEA版本比较新,或者安装了一些代码规范和检查的插件的话,我们不指定具体调用的话会给出具体的提示和解决方法的。

所以对于Java而言,我们无需担心代码会有菱形继承问题,不解决菱形继承问题,是无法通过编译阶段的!

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

java.util.Man

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值