java8学习:默认方法
内容来自《 java8实战 》,本篇文章内容均为非盈利,旨为方便自己查询、总结备份、开源分享。如有侵权请告知,马上删除。
书籍购买地址:java8实战
- 在java8中接口引进了静态方法以及默认方法,通过默认方法,可以为接口方法提供默认实现,也就是说接口能提供方法的具体实现.因此实现接口的类如果不显示的提供该方法的具体实现,就会自动继承默认的实现
- 同时定义接口以及工具辅助类是java常用的一种模式,工具类定义了与接口实例协作的很多静态方法,由于静态方法现在可以存在接口内部,所以代码中的辅助类就没有了存在的必要,可以直接把这些静态方法转移到接口内部
不断演进的API
- 我们来说一下为什么会出现默认方法
- 假如有如下一个接口,你是类库的设计者,那么你写的接口是这样的
public interface Behavior { void eat(String foodName); }
- 然后你发布之后大火,你的用户是这样使用的
public class Dog implements Behavior { @Override public void eat(String foodName) { System.out.println("dog " + foodName); } }
- 之后你收到了很多意见,说这个行为接口并不丰富,行为不可能只有吃的行为,动物可是都有吃喝拉撒的行为啊!
- 现在你就遇到了问题,如果你将建议的接口添加入Behavior接口,然后发布,这时候你的用户只要更新API的版本,那么他就会遇到一个大麻烦,那么就是实现你在接口中定义的所有方法.这对用户来说是非常不好的.这也是默认方法产生的原因:它可以让你放心的改进接口,无须担心遗留代码的影响,这是因为实现更新接口的类都会自动的集成一个默认的方法实现
不同类型的兼容:二进制,源代码和函数行为
-
变更对java的影响大体可以分为三种类型的兼容:二进制级的兼容,源代码级的兼容,以及函数行为的兼容.
- 二进制级的兼容性表示现有的二进制执行文件能无缝持续链接和运行,比如,为接口添加一个方法就是二进制级的兼容,这种方式下,如果添加的新方法不被调用,接口已经实现的方法可以继续运行,不会出现错误
- 简单的说,源代码级的兼容性表示引入变化之后,现有的程序依然能够成功通过编译
- 函数行为的兼容性表示发生变更后,程序接受同样的输入能得到相同的结果
概述默认方法
- 默认方法就是用default修饰的方法,并像类中声明的其他方法一样包含方法体,并且只要类实现了这个包含默认方法的接口,他就会继承默认方法
- java8中的抽象类和抽象接口区别:首先一个类只能继承一个抽象类,但是一个类可以实现多个接口,其次,一个抽象类可以通过实例变量保存一个通用状态,而接口是不能有实例变量的
默认方法的使用模式
-
可选模式
- 平常我们用类实现一个接口,接口中有很多方法需要我们重新定义,如果有用的方法还好,如果我们并用不到的方法,为了满足接口方法的实现规则,我们就必须在那放一个空方法实现,这也是多余的模板代码,我们可以将这类方法变更为默认方法以实现不必要的空实现
-
行为的多继承
- 行为的多继承值得是:类只能继承一个类,但是可以实现多个接口中的方法,这就是所谓的多继承,现在java8中有了方法的默认实现,那么我们的类就得到了来自不同接口的实现的功能
解决冲突的规则
-
一个类实现了多个接口,而多个接口中含有覆盖实现的方法,那么类会使用那个接口中的方法呢?如果是多个接口中的方法都是相同的方法签名呢?
interface A{ default void say(){ System.out.println("A"); } } interface B extends A { @Override default void say() { System.out.println("B"); } } public class Dog implements B{ public static void main(String[] args) { new Dog().say(); //B } }
-
如果一个类使用相同的函数签名从多个地方继承了方法,通过三条规则可以进行判断
- 类中的方法优先级最高,类或父类中声明的方法的优先级高于任何声明为默认方法的优先级
- 如果无法依据第一条判断,那么就是子类的优先级更高:函数签名相同时,优先选择拥有最具体实现的默认方法的接口,即B继承了A,那么B更具体
- 如果还没办法判断,继承多个接口的类必须通过显示覆盖和调用期望的方法,现实的选择使用哪个默认方法的实现
-
如下
interface A{ default void say(){ System.out.println("A"); } } interface B extends A { @Override default void say() { System.out.println("B"); } } class D implements A{ } public class Dog extends D implements A,B{ public static void main(String[] args) { new Dog().say(); //B } }
- 依照类或父类中声明的方法的优先级高于任何声明为默认方法的优先级,那么就会优先选择D,那么D并没有覆盖掉say方法,但是他默认继承了A的方法,所以它会调用A的方法,但是他会选择更具体的实现,那么就是B,最终也是输出的B
菱形继承问题
interface A{ default void say(){ System.out.println("A"); } } interface B extends A { } interface C extends A{ } public class Dog implements B,C{ public static void main(String[] args) { new Dog().say(); //A } }
- 如上,其继承实现关系类似于菱形,Dog实现了B,C但是BC中只是继承了A中的默认方法,所以编译并不会出错,并且Dog也是使用此默认实现,所以输出A
-
如果我们将B加上覆盖实现呢?
interface B extends A { default void say(){ System.out.println("B"); } }
- 现在会输出B,因为覆盖实现的方法更加具体
-
如果两个BC接口都覆盖实现了say方法呢?
//B接口如上变动 interface C extends A{ default void say(){ System.out.println("C"); } }
-
这时候我们就会发现,Dog编译出错了,因为BC都有具体实现,并且都是一个级别的,都是具体实现了A的默认方法,Dog就不知道需要调用谁的方法了,这时候我们只能是在Dog中覆盖这个默认方法的实现了
public class Dog implements B,C{ public static void main(String[] args) { new Dog().say(); //dog } @Override public void say() { System.out.println("dog"); } }
-
如果BC接口只是定义的与A中say方法签名一样的抽象方法呢?
interface A{ default void say(){ System.out.println("A"); } } interface B extends A { void say(); } interface C extends A{ default void say(){ System.out.println("C"); } } public class Dog implements B,C{ public static void main(String[] args) { new Dog().say(); //dog } @Override public void say() { System.out.println("dog"); } }
- 如上只要有一个接口有抽象方法,那么我们就还得按照接口中抽象方法必须实现的规则来
-
所以解决所有可能的冲突只需要三点
- 类或父类中显示声明的方法,其优先级高于任何默认方法,即自己实现的比默认的优先级高
- 如果上面的无法判断,方法签名又没有区别,那么选择提供最具体实现的默认方法的接口,即用接口的最具体的子类的实现
- 如果依旧有冲突,那么就只能在本类中重写覆盖默认方法了
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
如何学好 Linux、C++,并搞定 BAT 面试 作者/分享人:天千
学好Linux运维需要做到以下几点 1、多做实验 实验环境完全可以通过VMware来模拟,模拟私有网络,模拟多台机器,要搞懂VMware提供的集中网络模式的工作原理(桥接网络、宿主机网络、NAT等),这对整个Linux后续的学习帮助都非常之大,还可以通过虚拟机模拟Grub损坏并进行修复、模拟忘记密码并通过单用户模式修改密码等等。 2、LNMP、DHCP、DNS、MySQL等等这些网络服务需要完全基于源码来编译 这样更加有体感,对于编译的参数要理解,因为通过yum安装的软件包都是上一个稳定版本,并不是最新稳定版本,还有另外一点就是编译安装可以通过编译参数对软件进行一定的优化。 3、Linux基础要扎实 底层原理要理解,典型的文件系统的组成、inode和数据存放的位置、Linux进程是如何调度的、调度算法有哪些、磁盘调度算法有哪些、TCP/IP的三次握手和四次挥手的过程是如何的,网络中的数据是如何流向的(参考《构建高性能web站点》),iptables的三表五链、Nginx的网络IO模型(这个很重要,你要能讲清楚为什么Nginx要比Apache好),马哥Linux对于这个部分的内容讲...
- 下一篇
初学者Mybatis的初级使用
前言:本文章主要分三点内容对于Mybatis的初级使用进行教学,我们因为只是为了实现功能,而不是去了解底层封装原理,因此,我们只需要了解,Mybatis只是用来后端管理sql 管理dao层的框架即可!第一点:导框架包。第二点:配置文件第三点:对于dao接口实现的xml文件进行编写(管理sql) 正文 第一点: 因为我使用的是Maven的工程模式来进行系统的开发,因此我在导jar包的过程不需要去直接下载jar放到工程,然后再add build...而我因为使用Maven只需要在jar网上复制他的配置文件,Maven就可以直接帮我导下来使用: 在工程名.web中找到pom.xml中,找到意思是所有,在这个的范围内加入调用mybatis的架包: <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.5</version> </dependency> <dependen...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
-
Docker使用Oracle官方镜像安装(12C,18C,19C)
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS8编译安装MySQL8.0.19
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- MySQL8.0.19开启GTID主从同步CentOS8
- CentOS7,8上快速安装Gitea,搭建Git服务器
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
推荐阅读
最新文章
- Windows10,CentOS7,CentOS8安装Nodejs环境
- CentOS8编译安装MySQL8.0.19
- CentOS6,CentOS7官方镜像安装Oracle11G
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- 设置Eclipse缩进为4个空格,增强代码规范
- CentOS7,8上快速安装Gitea,搭建Git服务器
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- CentOS7安装Docker,走上虚拟化容器引擎之路
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题