Effective Java 第三版——21. 为后代设计接口
Tips
《Effective Java, Third Edition》一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将近8年的时间,但随着Java 6,7,8,甚至9的发布,Java语言发生了深刻的变化。
在这里第一时间翻译成中文版。供大家学习分享之用。

21. 为后代设计接口
在Java 8之前,不可能在不破坏现有实现的情况下为接口添加方法。 如果向接口添加了一个新方法,现有的实现通常会缺少该方法,从而导致编译时错误。 在Java 8中,添加了默认方法( default method)构造[JLS 9.4],目的是允许将方法添加到现有的接口。 但是增加新的方法到现有的接口是充满风险的。
默认方法的声明包含一个默认实现,该方法允许实现接口的类直接使用,而不必实现默认方法。 虽然在Java中添加默认方法可以将方法添加到现有接口,但不能保证这些方法可以在所有已有的实现中使用。 默认的方法被“注入(injected)”到现有的实现中,没有经过实现类的知道或同意。 在Java 8之前,这些实现是用默认的接口编写的,它们的接口永远不会获得任何新的方法。
许多新的默认方法被添加到Java 8的核心集合接口中,主要是为了方便使用lambda表达式(第6章)。 Java类库的默认方法是高质量的通用实现,在大多数情况下,它们工作正常。 但是,编写一个默认方法并不总是可能的,它保留了每个可能的实现的所有不变量。
例如,考虑在Java 8中添加到Collection接口的removeIf方法。例如,考虑在Java 8中添加到Collection接口的removeIf方法。此方法删除给定布尔方法(或Predicate函数式接口)返回true的所有元素。默认实现被指定为使用迭代器遍历集合,调用每个元素的谓词,并使用迭代器的remove方法删除谓词返回true的元素。 据推测,这个声明看起来像这样:默认实现被指定为使用迭代器遍历集合,调用每个元素的Predicate函数式接口,并使用迭代器的remove方法删除Predicate函数式接口返回true的元素。 根据推测,这个声明看起来像这样:
// Default method added to the Collection interface in Java 8
default boolean removeIf(Predicate<? super E> filter) {
Objects.requireNonNull(filter);
boolean result = false;
for (Iterator<E> it = iterator(); it.hasNext(); ) {
if (filter.test(it.next())) {
it.remove();
result = true;
}
}
return result;
}
这是可能为removeIf方法编写的最好的通用实现,但遗憾的是,它在一些实际的Collection实现中失败了。 例如,考虑org.apache.commons.collections4.collection.SynchronizedCollection方法。 这个类出自Apache Commons类库中,与java.util包中的静态工厂Collections.synchronizedCollection方法返回的类相似。 Apache版本还提供了使用客户端提供的对象进行锁定的能力,以代替集合。 换句话说,它是一个包装类(条目 18),它们的所有方法在委托给包装集合类之前在一个锁定对象上进行同步。
Apache的SynchronizedCollection类仍然在积极维护,但在撰写本文时,并未重写removeIf方法。 如果这个类与Java 8一起使用,它将继承removeIf的默认实现,但实际上不能保持类的基本承诺:自动同步每个方法调用。 默认实现对同步一无所知,并且不能访问包含锁定对象的属性。 如果客户端在另一个线程同时修改集合的情况下调用SynchronizedCollection实例上的removeIf方法,则可能会导致ConcurrentModificationException异常或其他未指定的行为。
为了防止在类似的Java平台类库实现中发生这种情况,比如Collections.synchronizedCollection返回的包级私有的类,JDK维护者必须重写默认的removeIf实现和其他类似的方法来在调用默认实现之前执行必要的同步。 原来不属于Java平台的集合实现没有机会与接口更改进行类似的改变,有些还没有这样做。
在默认方法的情况下,接口的现有实现类可以在没有错误或警告的情况下编译,但在运行时会失败。 虽然不是非常普遍,但这个问题也不是一个孤立的事件。 在Java 8中添加到集合接口的一些方法已知是易受影响的,并且已知一些现有的实现会受到影响。
应该避免使用默认方法向现有的接口添加新的方法,除非这个需要是关键的,在这种情况下,你应该仔细考虑,以确定现有的接口实现是否会被默认的方法实现所破坏。然而,默认方法对于在创建接口时提供标准的方法实现非常有用,以减轻实现接口的任务(条目 20)。
还值得注意的是,默认方法不是被用来设计,来支持从接口中移除方法或者改变现有方法的签名的目的。在不破坏现有客户端的情况下,这些接口都不可能发生更改。
准则是清楚的。 尽管默认方法现在是Java平台的一部分,但是非常悉心地设计接口仍然是非常重要的。 虽然默认方法可以将方法添加到现有的接口,但这样做有很大的风险。 如果一个接口包含一个小缺陷,可能会永远惹怒用户。 如果一个接口严重缺陷,可能会破坏包含它的API。
因此,在发布之前测试每个新接口是非常重要的。 多个程序员应该以不同的方式实现每个接口。 至少,你应该准备三种不同的实现。 编写多个使用每个新接口的实例来执行各种任务的客户端程序同样重要。 这将大大确保每个接口都能满足其所有的预期用途。 这些步骤将允许你在发布之前发现接口中的缺陷,但仍然可以轻松地修正它们。 虽然在接口被发布后可能会修正一些存在的缺陷,但不要太指望这一点。
Effective Java 第三版——21. 为后代设计接口的更多相关文章
- Effective Java 第三版——14.考虑实现Comparable接口
Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...
- Effective Java 第三版——64. 通过对象的接口引用对象
Tips 书中的源代码地址:https://github.com/jbloch/effective-java-3e-source-code 注意,书中的有些代码里方法是基于Java 9 API中的,所 ...
- 《Effective Java 第三版》目录汇总
经过反复不断的拖延和坚持,所有条目已经翻译完成,供大家分享学习.时间有限,个别地方翻译得比较仓促,希望有疑虑的地方指出批评改正. 第一章简介 忽略 第二章 创建和销毁对象 1. 考虑使用静态工厂方法替 ...
- 《Effective Java 第三版》新条目介绍
版权声明:本文为博主原创文章,可以随意转载,不过请加上原文链接. https://blog.csdn.net/u014717036/article/details/80588806前言 从去年的3月份 ...
- effective Java 第三版学习笔记
创建对象类型的 1,静态工厂方法代替构造器 静态工厂方法有名称,不容易混乱他的作用 不必再每次调用他的时候创建实例,创建实例的代价是高的,可以重复利用缓存的对象 静态工厂甚至能返回子类对象,例如在接口 ...
- Effective Java 第三版——10. 重写equals方法时遵守通用约定
Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...
- Effective Java 第三版—— 20. 接口优于抽象类
Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...
- Effective Java 第三版——34. 使用枚举类型替代整型常量
Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...
- Effective Java 第三版——44. 优先使用标准的函数式接口
Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...
随机推荐
- html5笔记——<section> 标签
定义和用法 <section> 标签定义文档中的节(section.区段).比如章节.页眉.页脚或文档中的其他部分. 注意: section 不是一个专用来做容器的标签,如果仅仅是用于设置 ...
- [.Net跨平台]部署DTCMS到Jexus遇到的问题及解决思路---Linux环境搭建
最近朋友托我帮忙研究如何把一个DTCMS部署到Linux下,经过1天的研究,部署基本成功,可能有些细节还未注意到,现在把心得分享一下.过程比预期的要简单 身为.Net程序员,这个问题的第一步可能就是如 ...
- 微信小程序教学第四章第二节(含视频):小程序中级实战教程:详情-视图渲染
§ 详情 - 数据渲染 本文配套视频地址: https://v.qq.com/x/page/x055550lrvd.html 开始前请把 ch4-2 分支中的 code/ 目录导入微信开发工具 这一节 ...
- Pycharm配置(一)
Pycharm作为一款强力的Python IDE,在使用过程中感觉一直找不到全面完整的参考手册,因此决定对官网的Pycharm教程进行简要翻译,与大家分享. 1.准备工作 官网下载 2.如何选择Pyc ...
- iOS常见的几种加密方法(base64.MD5.Token传值.系统指纹验证。。加密)
普通加密方法是讲密码进行加密后保存到用户偏好设置中 钥匙串是以明文形式保存,但是不知道存放的具体位置 一. base64加密 base64 编码是现代密码学的基础 基本原理: 原本是 8个bit 一组 ...
- Python Web框架
本节对Python Web框架学习 一.MTVModel: 存放所有数据库相关文件Template:模板文件,存放html文件View: 业务处理,即函数文件 二.MVCmodel: 存放数据库相关文 ...
- JavaWeb之数据源连接池(4)---自定义数据源连接池
[续上文<JavaWeb之数据源连接池(3)---Tomcat>] 我们已经 了解了DBCP,C3P0,以及Tomcat内置的数据源连接池,那么,这些数据源连接池是如何实现的呢?为了究其原 ...
- CDQ分治与整体二分小结
前言 这是一波强行总结. 下面是一波瞎比比. 这几天做了几道CDQ/整体二分,感觉自己做题速度好慢啊. 很多很显然的东西都看不出来 分治分不出来 打不出来 调不对 上午下午晚上的效率完全不一样啊. 完 ...
- form表单与后台请求的关系
开发中遇到一个问题,说这个问题前先看一下代码 后台方面, get请求: post请求: 前端方面: 问题是:当我点击提交表单后,页面会跳转成这样: 经过多番测试,原因竟是form表单的提交问题,如果用 ...
- 解决Windows和Linux使用npm打包js和css文件不同的问题
1.问题出现 最近公司上线前端H5页面,使用npm打包,特别奇怪的是每次打包发现css和js文件与Windows下打包不一致(网页使用Windows环境开发),导致前端页面功能不正常. 2.问题排查 ...