栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

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

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

一文学会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");
    }
}

而Father1和Father2分别继承了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子类,同时实现Father1和Father2接口

public class Son implements Father1,Father2{

}

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

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

输出结果(报错):

/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{
 
}

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

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

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

执行结果如下:

hello,I'm Father1

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

回到初始的状态,Son类实现Father1和Father2,

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

public class Son implements Father1,Father2{

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

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

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

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

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

补充:

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

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

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/697047.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号