Java 8默认方法可能会破坏您的(用户)代码

乍一看, 默认方法为Java虚拟机的指令集带来了一个很棒的新功能。 最后,库开发人员能够开发已建立的API,而不会对其用户代码造成不兼容性。 使用默认方法,当将新方法引入该接口时,任何实现库接口的用户类都会自动采用默认代码。 而且,一旦用户更新了他的实现类,他就可以使用对他的特定用例更有意义的东西来覆盖默认值。 更好的是,用户可以从重写的方法中调用接口的默认实现,并在其周围添加逻辑。

到目前为止,一切都很好。 但是,向已建立的接口添加默认方法会使Java代码无法编译。 在查看示例时,这是最容易理解的。 让我们假设一个库需要其接口之一的类作为输入:

interface SimpleInput {void foo();void bar();
}abstract class SimpleInputAdapter implements SimpleInput {@Overridepublic void bar() {// some default behavior ...}
}

在Java 8之前,接口和相应适配器类的上述组合是Java编程语言中相当普遍的模式。 图书馆供应商通常会提供适配器,以节省图书馆用户的键入时间。 但是,还额外提供了接口以允许多重继承的近似。

让我们进一步假设用户使用了此适配器:

class MyInput extends SimpleInputAdapter{@Overridepublic void foo() {// do something ...}@Overridepublic 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 {@Overridepublic void foo() {// do something ...}@Overridepublic void bar() {SimpleInput.super.foo();// do something additionally ... }
}

为了实现与原始类中类似的行为,我们利用Java 8的新语法来调用特定接口的默认方法。 另外,我们将myMethod的逻辑移到了一些基类MyBase 。 拍拍我们的肩膀。 很好的重构!

我们正在使用的库取得了巨大的成功。 但是,维护人员需要添加另一个接口以提供更多功能。 此接口表示一个CompexInput ,它使用其他方法扩展了SimpleInput 。 因为默认方法通常被认为可以安全添加 ,所以维护者还重写了SimpleInput的默认方法,并添加了一些行为以提供更好的默认值。 毕竟,在实现适配器类时,这样做很普遍:

interface ComplexInput extends SimpleInput {void qux();@Overridedefault void bar() {SimpleInput.super.bar(); // so complex, we need to do more ...}
}

这项新功能非常强大,以至ThirdPartyBaseClass的维护者决定也依赖此库。 为此,他为ThirdPartyLibrary实现了ComplexInput接口。

但这对MyInput类意味着什么? 由于通过扩展ThirdPartyBaseClass隐式实现ComplexInput ,调用SimpleInput的默认方法突然变得非法。 结果,用户的代码不再编译。 而且,由于Java认为此调用与调用间接超类的super-super方法一样是非法的,因此现在通常也禁止调用此方法。 相反,您可以调用默认方法
ComplexInput类。 但是,这需要您首先在MyInput显式实现此接口。 对于图书馆的用户来说,这种变化很有可能是出乎意料的!

奇怪的是,Java运行时没有进行这种区分。 JVM的验证程序将允许已编译的类调用SimpleInput::foo即使已加载的类在运行时通过扩展ThirdPartyBaseClass的更新版本隐式实现了ComplexClass 。 这里只有编译器抱怨。

但是我们从中学到什么呢? 简而言之,请确保不要在另一个接口中覆盖默认方法。 既不使用其他默认方法,也不使用抽象方法。 通常,要小心使用默认方法。 它们可以像Java的collection接口一样轻松地简化已建立的API的演变,但它们本身却很复杂,因为它们允许执行类型层次结构中的方法调用。 在Java 7之前,您只需要通过遍历线性类层次结构来查找实际调用的代码。 仅当您确实觉得有必要时才添加这种复杂性。

翻译自: https://www.javacodegeeks.com/2014/05/java-8-default-methods-can-break-your-users-code.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/363279.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ADO.NET之使用DataSet类更新数据库

1.首先从数据库获得数据填充到DataSet类,该类中的表和数据库中的表相互映射。 2.对DataSet类中的表进行修改(插入,更新,删除等) 3.同步到数据库中:使用SqlDataAdapter实例名.Update(DataSet实例名&#xff…

CSS完美兼容IE6/IE7/FF的通用方法

关于CSS对各个浏览器兼容已经是老生常谈的问题了, 网络上的教程遍地都是.以下内容没有太多新颖, 希望能对初学者有一定的帮助.一、CSS HACK 以下两种方法几乎能解决现今所有HACK. 1, !important 随着IE7对!important的支持, !important 方法现在只针对IE6的HACK.(注意写法.记得…

学习进度

这一周学习了第四章登录,第五章系统业务逻辑没有完成上周规定的四五六章,因为发现有些以前看的东西现在没印象了,又看了一些之前的。 在代码上完成了老师布置的代码,第一个是判断AB和C的关系 ,第二个是成绩排名&#x…

为Lucene选择快速唯一标识符(UUID)

大多数使用Apache Lucene的搜索应用程序都会为每个索引文档分配唯一的ID(即主键)。 尽管Lucene本身不需要这样做(它可能不太在乎!),但应用程序通常需要它以后通过其外部ID替换,删除或检索该文档…

ubuntu16.04设置静态ip

最近在课堂上,有很多同学反映在搭建环境的时候,虚拟机ip经常变,那么我们配置好的web服务可能就不能用了。下面讲一下如何在ubuntu上面设置静态ip 1:首先我们确认一下ubuntu的版本 cat /etc/issue 或者sudo lsb_release -a或者unam…

css布局:块级元素的居中

一.定宽&#xff1a; 1.定位居中(absolute) 方法一&#xff1a; html:<div class"main"></main>css:.main{width:400px;height:200px;background:#eee;position:absolute;left:50%;top:50%;margin-left:-200px;margin-top:-100px;} 方法二&#xff1a;…

精准客户数据服务

详情请点击&#xff1a;http://www.rulingtech.com 客户数据&#xff08;名录&#xff09;是市场营销活动不可或缺的重要资料。目前企业采用的客户名录数据主要通过订购黄页、购买第三方名录、自行搜集等途径。这些方式面临的问题是&#xff1a;数据重复度高、有效性差、准确率…

Maven常用的构建命令

Maven常用命令&#xff1a; Maven库&#xff1a; http://repo2.maven.org/maven2/ Maven依赖查询&#xff1a; http://mvnrepository.com/ 一&#xff0c;Maven常用命令&#xff1a; 1. 创建Maven的普通Java项目&#xff1a; mvn archetype:create-DgroupIdpackageName-Dartifa…

JPA 2.1实体图–第1部分:命名实体图

延迟加载通常是JPA 2.0的问题。 如果要使用FetchType.LAZY&#xff08;默认&#xff09;或FetchType.EAGER来加载关系&#xff0c;则必须在实体上进行定义&#xff0c;并且始终使用此模式。 仅当我们要始终加载关系时才使用FetchType.EAGER。 FetchType.LAZY几乎在所有情况下都…

课时85.层叠性(掌握)

1.什么是层叠性&#xff1f; 层叠性就是CSS处理冲突的一种能力。 这个字体最终会变为红色 注意点&#xff1a; 层叠性只有在多个选择器选中“同一个标签”,然后又设置了“相同的属性”&#xff0c;才会发生层叠性。 CSS全称&#xff1a;Cascading StyleSheet 层叠样式表&am…

ABZ职业规划

关于职业规划&#xff0c;LinkedIn&#xff08;领英&#xff09;创始人里德霍夫曼&#xff08;Reid Hoffman&#xff09;提出著名的“ABZ理论”&#xff0c;教我们应对变化莫测的职场人生。 德霍父曼建议每个职场人应有A计划&#xff0c;B计划&#xff0c;C计划。 A计划&#x…

SetProcessWorkingSetSize减少内存占用

系统启动起来以后&#xff0c;内存占用越来越大&#xff0c;使用析构函数、GC.Collect什么的也不见效果&#xff0c;后来查了好久&#xff0c;找到了个办法&#xff0c;就是使用 SetProcessWorkingSetSize函数。这个函数是Windows API 函数。下面是使用的方法&#xff1a;[Syst…

Spring Boot 与消息 (JMS、AMQP、RabbitMQ)

RabbitMQ教程 - 鸟哥的专栏 - CSDN博客 一、概述 大多应用中&#xff0c;可通过消息服务中间件来提升系统异步通信、扩展解耦能力消息服务中两个重要概念&#xff1a;消息代理&#xff08;message broker)和目的地&#xff08;destination) 当消息发送者发送消息以后&#xff0…

使用Apache Camel通过soap添加WS-Security

WS-Security&#xff08;Web服务安全性&#xff09;是一种协议&#xff0c;可让您保护自己的soap Web服务。 发出Soap请求的客户端必须在Soap标头中提供登录名和密码。 服务器接收到肥皂请求&#xff0c;检查凭据并验证请求是否正确。 使用Apache Camel&#xff0c;可以很容易…

课时3.浏览器访问网页原理(理解)

浏览器访问网页原理&#xff08;理解&#xff09; 第一次打开IE6&#xff0c;发现系统自动生成了一个文件夹&#xff0c;所以我们可以得出这个文件夹必然和IE6有一定的关系先删除Internet Cache下的所有文件夹&#xff0c;然后通过IE6打开百度的首页&#xff0c;打开百度首页以…

matlab 图像平移操作

目标&#xff1a;对原图I进行[80,50]的偏移操作得到图B。 首先读入图像&#xff0c;以matlab自带的pout.tif为例. strel是创建形态学结构元素的. translate函数在原结构上进行[80,50]的偏移. I imread(cameraman.tif); se translate(strel(1),[80,50]); B imdilate(I,se);转…

爬虫之基于线程池异步抓取

from multiprocessing.dummy import Pool #线程池模块#必须只可以有一个参数 def my_requests(url):return requests.get(urlurl,headersheaders).textstart time.time() urls [http://127.0.0.1:5000/bobo,http://127.0.0.1:5000/jay,http://127.0.0.1:5000/tom, ]pool Poo…

使用xjc一秒钟生成您的JAXB类

由于JAXB是JDK的一部分&#xff0c;因此它是处理XML文档最常用的框架之一。 它提供了一种从XML文档检索数据并将其存储到Java类的简便方法。 因为几乎每个Java开发人员都已经使用过JAXB&#xff0c;所以我不会解释不同的JAXB批注。 相反&#xff0c;我将专注于一个名为xjc的命令…

课时27.base(掌握)

base标签就是专门用来统一的指定当前网页中所有的超链接&#xff08;a标签&#xff09;需要如何打开格式 <base target"_blank"> <a href"https://www.baidu.com">百度</a> 注意点&#xff1a; base标签必须写在head标签的开始标签和…

async await详解

async await本身就是promise generator的语法糖。 本文主要讲述以下内容 async awiat 实质async await 主要特性async await 实质 下面使用 promise generate 实现 async await // 转换目标 async1// async function async1() {// console.log(async1 start);// await …