JAVA移慎_谨慎使用Java8的默认方法
为什么要谨慎使用Java8的默认方法?本文给出了为什么要慎用Java8默认方法的原因,解释的很详细,感兴趣的朋友可以参考一下
默认方法给JVM的指令集增加了一个非常不错的新特性。使用了默认方法之后,如果库中的接口增加了新的方法,实现了这个接口的用户类能够自动获得这个方法的默认实现。一旦用户想更新他的实现类的话,只需覆盖一下这个默认方法就可以了,取而代之的是一个在特定场景下更有意义的实现。更棒的是,用户可以在重写的方法里面调用接口的默认实现来增加一些额外的功能。
目前为止一切都还不错。然而,给现有的Java接口增加默认方法可能会导致代码的不兼容。看个例子就很容易能明白了。假设有一个库,它需要用户实现它的一个接口作为输入:
interface SimpleInput {
void foo();
void bar();
}
abstract class SimpleInputAdapter implements SimpleInput {
@Override
public void bar() {
// some default behavior ...
}
}
在Java 8以前,上述这种接口和一个对应的适配器类的组合在Java语言中是一种很常见的模式。类库的开发人员提供了一个适配器来减少库使用者的编码量。然而提供这个接口的目的其实是为了能实现某种类似多重继承的关系。
我们假设有一个用户使用了这个适配器:
class MyInput extends SimpleInputAdapter{
@Override
public void foo() {
// do something ...
}
@Override
public void bar() {
super.bar();
// do something additionally ...
}
}
有了这个实现,用户可以和库进行交互了。注意这个实现是如何重写bar方法来给默认的实现增加额外的功能的。
那如果这个库迁移到Java 8的话会怎样?首先,这个库很可能会废弃掉这个适配器类并将这个功能迁移到默认方法里。最终这个接口看起来会是这样的:
interface SimpleInput {
void foo();
default void bar() {
// some default behavior
}
}}
有了这个新接口后,用户得更新他的代码来使用这个默认方法,而不再是适配器类了。使用新接口而非适配器类的一大好处就是,用户可以去继承一个别的类而不是这个适配器类了。我们来动手实践一下,将MyInput类改造成使用默认方法。由于现在我们可以继承别的类了,我们再额外地扩展一个第三方的基类试试。这个基类具体是做什么的在这里并不重要,我们先假设一下这么做对我们这个用例来说是有意义的。
class MyInput extends ThirdPartyBaseClass implements SimpleInput {
@Override
public void foo() {
// do something ...
}
@Override
public void bar() {
SimpleInput.super.foo();
// do something additionally ...
}
}
为了实现和原先那个类同样的功能,这里我们用到了Java 8的新语法来调用接口的默认方法。同样的,我们把myMethod的逻辑放到某个基类MyBase里面。可以捶捶肩膀放松下了。重构之后棒极了!
我们使用的这个库得到了很大的改进。然而,维护人员需要添加另一个接口来实现一些额外的功能。这个接口叫做CompexInput ,它继承了SimpleInput类,并增加了一个额外的方法。由于通常都认为默认方法是可以放心地添加的,因此维护人员重写了SimpleInput类的默认方法并添加了一些额外的动作来给用户提供一个更好的默认实现。毕竟使用适配器类的时候这个做法也十分常见:
interface ComplexInput extends SimpleInput {
void qux();
@Override
default void bar() {
SimpleInput.super.bar();
// so complex, we need to do more ...
}
}
这个新特性看起来非常不错,因此ThirdPartyBaseClass类的维护人员也决定使用这个库了。为了实现这个,他将ThirdPartyBaseClass类实现了ComplexInput接口。
但这样的话对MyInput类意味着什么?由于它继承了ThirdPartyBaseClass类,因此默认实现了ComplexInput接口,这样的话调用SimpleInput的默认方法就不合法了。结果就是,用户的代码最后无法通过编译。还有就是,现在已经彻底无法调用这个方法了,因为Java把这种调用间接父类的super-super方法认为是不合法的。你只能去调用ComplexInput接口的默认方法了。然而这首先需要你在MyInput类中显式的实现一下这个接口。对于这个库的用户而言,这些改动完全是意想不到的。
(注:简单点说其实就是:
interface A {
default void test() {
}
}
interface B extends A {
default void test() {
}
}
public class Test implements B {
public void test() {
B.super.test();
//A.super.test(); 错误
}
}
当然这么写的话是用户主动选择实现了B接口,而文中的例子由于引入了一个基类,因此由于库和基类中都进行了一个看似没有影响的改动,实际上却导致用户代码无法通过编译)
很奇怪的是,Java在运行时并没有对这个进行区分。JVM的校验器允许一个编译过的类进行SimpleInput::foo方法的调用,尽管加载的这个类继承了ThirdPartyBaseClass的更新版本后隐式地实现了ComplexInput接口。要怪只能怪编译器了。(注:编译器与运行时的行为不一致)
那我们从中学到了什么?简单地说,不要在另一个接口中重写原接口的默认方法。不要用另一个默认方法来重写它,也不要某个抽象方法来重写它。总而言之,使用默认方法时应当十分谨慎。虽然它们使得Java现有的集合库的接口更容易改进了,但它允许你在类的继承结构中进行方法调用,这本质上其实是增加了复杂性。在Java 7以前,你只需遍历线性的类层次结构看一下实际调用的代码就可以了。当你觉得的确需要的时候,再去使用默认方法。
以上就是针对为什么要慎用Java8的默认方法进行的详细解释,希望对大家的学习有所帮助。
JAVA移慎_谨慎使用Java8的默认方法相关推荐
- Java8 - 接口默认方法
2019独角兽企业重金招聘Python工程师标准>>> Java8 - 接口默认方法 什么是默认方法,为什么要有默认方法 简单说,就是接口可以有实现方法,而且不需要实现类去实现其方法 ...
- 30分钟入门Java8之默认方法和静态接口方法
2019独角兽企业重金招聘Python工程师标准>>> 30分钟入门Java8之默认方法和静态接口方法 作者:@JohnTsai 本文为作者原创,转载请注明出处:http://www ...
- JAVA移慎_java里面给对象赋值,慎用赋值符号(=) (转)
java里面给对象赋值,慎用赋值符号(=) (转)[@more@] 对于来说,注意变量名类似于指针,所以请慎用赋值符号(=)! 比如下面的代码: ArrayList alAll = new Array ...
- java map合并_详解Java8合并两个Map中元素的正确姿势
1. 介绍 本入门教程将介绍Java8中如何合并两个map. 更具体说来,我们将研究不同的合并方案,包括Map含有重复元素的情况. 2. 初始化 我们定义两个map实例 private static ...
- java越权发送邮件_水平越权的常见解决方法
场景模拟 场景一 只允许资源的所有者才能对资源进行操作(CRUD).比如,jack在某博客平台写了一篇私密文章,只有自己可以对这篇文章进行增删查改的操作: 场景二 允许指定个人或者角色也能对资源进行操 ...
- java 获取上下文_如何获得spring上下文的方法总结
一 前言 打算重温spring,以后可能每周会发一篇吧,有空就搞搞: 二 获取上下文的几种方式 AnnotationConfigApplicationContext:从一个或多个基于Java的配置类中 ...
- java date 过时_过时date.toLocaleString()的解决方法
过时date.toLocaleString()的解决方法 Java代码 收藏代码 System.out.println(new java.util.Date()); 输出:Thu Jan 27 ...
- 谷歌浏览器如何启用java小脚本_各种浏览器开启JavaScript脚本方法
随着网站设计技术的发展,为了用户友好体验,大部分网站使用了JavaScript脚本设计,如果您的浏览器禁用或关闭的JavaScript支持,那么可能造成网站体验差或网站部分功能无法使用 下面提供10种 ...
- java arraylist遍历_遍历ArrayList的4种方法
以下4种方法有什么不同,个人感觉好象没什么不同..除了用跌代器看上去比较帅以外.... package com.test; import java.util.ArrayList; import jav ...
最新文章
- Android开发中应避免的重大错误
- WAIC开发者日倒计时两天,收藏好这份完整日程
- 十二五期间三网融合将有实质性进展
- linux提示链接层次太多,嵌入式linuxmusic播放器
- 有了 IP 地址,为什么还要用 MAC 地址?
- 洛谷入门题P1008、P1035、P1423、P1424、P1980题解(Java语言描述)
- dw显示云服务器的数据库,dw如何连接云服务器
- 计算机网络培养方案,计算机网络技术专业培养方案
- FisherYates费雪耶兹随机置乱算法
- 串口屏和并口屏的区别?
- pycharm中快捷键新建文件,pycharm快捷键
- pb中数据窗口函数小结(转)
- 周围剃光头顶留长发型_为什么很多秃顶的人,宁可留周围一圈头发,也不直接剃成光头?...
- 北京理工大学计算机实验广域网通信与有,北京理工大学计算机实验七报告表.doc...
- Android JetPack底部导航Navigation 组件的介绍与使用
- 互联网上下50年,万字长文推演Web1.0到Web5.0
- 推荐10本程序员必读的书籍!
- ARM树莓派高级开发——linux内核源码、树莓派源码编译、SD卡挂载
- 我才22岁,我再玩一年又能怎么样?等我23岁的时候,一定给你活出个人样
- Java编程应用(六):新浪新闻爬虫程序