您需要了解的有关默认方法的所有信息

因此,默认方法是……昨天的新闻,对不对? 是的,但是使用一年后,积累了很多事实,我想将这些事实收集在一个地方,供刚开始使用它们的开发人员使用。 甚至有经验的人都可以找到他们不知道的一两个细节。

如果有新发现,我将在将来扩展此职位。 因此,我要我的读者(是的,你们两个!)向我提供有关默认方法的每个小事实,您在这里找不到这些方法。 如果您有任何东西,请鸣叫 , 发邮件或发表评论。

总览

我想我没有给这篇文章一个有意义的叙述。 原因在于,从本质上讲,这是一篇Wiki文章。 它涵盖了默认方法的不同概念和细节,尽管它们与自然相关,但它们并不适合进行连续叙述。

但这也有好处! 您可以轻松地在文章中跳来跳去,而不会大大降低您的阅读体验。 查看目录,以全面了解所涵盖的内容,并了解您的好奇心将您带到何处。

默认方法

到现在为止,大多数开发人员将已经使用,阅读甚至实现默认方法,因此,我将为每个人省去语法的详细介绍。 在介绍更广泛的概念之前,我将花更多时间在它的细节上。

句法

默认方法的新语言功能归结为接口现在可以声明非抽象方法,即带有主体的方法。

以下示例是JDK 8中Comparator.thenComparing(Comparator)( link )的修改版:

比较器中的默认方法

default Comparator<T> thenComparing(Comparator<? super T> other) {return (o1, o2) -> {int res = this.compare(o1, o2);return (res != 0) ? res : other.compare(o1, o2);};
}

除了关键字default之外,这看起来像一个“常规”方法声明。 将这样的方法添加到接口而没有编译错误并提示方法调用解决策略是必要的。

现在,实现Comparator每个类都将包含thenComparing(Comparator)公共方法,而不必自己实现-可以这么说,它是免费的。

显式调用默认方法

在下面的内容中,我们将看到一些人们可能想要从某个特定的超级接口显式调用方法的默认实现的原因。 如果有需要,请按照以下步骤进行:

明确调用默认实现

class StringComparator implements Comparator<String> {// ...@Overridepublic Comparator<String> thenComparing(Comparator<? super String> other) {log("Call to 'thenComparing'.");return Comparator.super.thenComparing(other);}
}

请注意,接口的名称如何用于指定以下super ,否则将引用超类(在本例中为Object )。 从句法上讲,这类似于可以从嵌套类访问对外部类的引用 。

解决策略

因此,让我们考虑一个使用默认方法实现接口的类型的实例。 如果调用存在默认实现的方法会怎样? (请注意,方法由其签名标识,该签名包括名称和参数类型。)

规则1
类胜过接口。 如果超类链中的一个类具有方法的声明(具体的或抽象的),则说明操作已完成,默认设置无关紧要。
规则2
更加具体的接口将胜过较不具体的接口(此处的特异性表示“子类型化”)。 无论ListCollection在何处,如何或多少次进入继承图, List的默认值都将胜过Collection的默认值。
规则3
没有规则#3。 如果根据上述规则没有唯一的获胜者,则具体班级必须手动消除歧义。

Brian Goetz – 2013年3月3日(格式化我的)

首先,这阐明了为什么将这些方法称为默认方法以及为什么必须使用关键字default启动它们:

这样的实现是备用的,以防万一一个类并且其超类都不考虑该方法,即不提供任何实现,也不将其声明为抽象(请参见规则#1 )。 等效地,仅在类未实现扩展X并声明相同方法的接口Y (默认或抽象;请参见规则#2 )时,才使用接口X的默认方法。

尽管这些规则很简单,但是它们并不能阻止开发人员创建复杂的情况。 这篇文章提供了一个示例,其中分辨率的预测并非微不足道,并提出了应谨慎使用此功能的说法。

解决策略包含一些有趣的细节……

解决冲突

规则#3 ,或者说它的缺失,意味着具体的类必须实现存在竞争默认实现的每个方法。 否则,编译器将引发错误。 如果有竞争的实现之一是合适的,则方法主体可以显式调用该方法 。

这也意味着向接口添加默认实现会导致编译错误。 如果一个类A实现无关接口XY并且其已经存在于默认方法X被添加到Y ,类A将不再编译。

如果未AXY一起编译,并且JVM偶然发现这种情况,会发生什么情况? 有趣的问题, 答案似乎还不清楚 。 看起来JVM将抛出IncompatibleClassChangeError。

重新提取方法

如果抽象类或接口A某个方法声明为抽象,而某些超级接口X存在默认实现,则X的默认实现将被覆盖。 因此,所有子类A具体类都必须实现该方法。 这可以用作强制执行不合适的默认实现的有效工具。

此技术在整个JDK中使用,例如在ConcurrentMap ( link )上使用,该方法重新抽象了Map ( link )提供默认实现的许多方法,因为它们不是线程安全的(搜索术语“不合适的默认值”)。

请注意,具体类仍可以选择显式调用重写的默认实现 。

“对象”上的覆盖方法

接口不可能为Object的方法提供默认实现。 尝试这样做将导致编译错误。 为什么?

首先,这将是无用的。 由于每个类都继承自Object ,因此规则1明确表示永远不会调用这些方法。

但是那条规则不是自然法则,专家组本可以例外。 邮件中还包含规则, Brian Goetz给出了为什么没有规则的许多原因 。 我最喜欢的一个(格式化我的):

从根本toString ,来自Object的方法(例如toStringequalshashCode )都是关于对象的state的 。 但是接口没有状态。 类有状态。 这些方法属于拥有对象状态的代码-类。

修饰符

请注意,有很多修饰符不能在默认方法上使用:

  • 可见性固定为公开(与其他界面方法一样)
  • 关键字synchronized禁止(如在抽象方法)
  • 关键字final是被禁止的(与抽象方法一样)

当然,需要这些功能,并且对它们的缺失进行了全面的解释(例如,针对final和synced )。 参数总是相似的:这不是默认方法的目的 ,引入这些功能将导致更复杂且易于出错的语言规则和/或代码。

不过,您可以使用static ,这将减少对复数形式的实用程序类的需求 。

一点上下文

现在我们已经知道了如何使用默认方法,下面将这些知识放到上下文中。

由F_A在CC-BY 2.0下发布。

由F_A在CC-BY 2.0下发布 。

接口演变

经常可以找到介绍默认方法的专家组,他们指出他们的目标是允许“接口演变”:

默认方法 […]的目的是使接口在首次发布后能够以兼容的方式进行开发。

Brian Goetz – 2013年9月

在使用默认方法之前,几乎不可能(不包括某些组织模式;请参阅此概述 )在不破坏所有实现的情况下向接口添加方法。 尽管这对于控制这些实现的绝大多数软件开发人员都无关紧要,但对于API设计人员而言,这是一个至关重要的问题。 Java始终保持安全,在发布接口后也从未更改过接口。

但是随着引入lambda表达式,这变得难以忍受。 想象一下总是编写Stream.of(myList).forEach(...)的集体痛苦,因为无法将forEach添加到List

因此,引入了lambdas的专家组决定寻找一种方法,以在不破坏任何现有实现的情况下实现接口演化。 他们对这一目标的关注解释了默认方法的特征 。

在小组认为有可能不降低该主要用例的可用性的情况下,他们还启用了使用默认方法来创建特征的方法,或者甚至是接近它们的方法。 尽管如此,他们还是因为没有“一路走到”混合蛋白和特质而经常受到攻击,对此人们经常重复回答:“是的,因为那不是我们的目标。”

实用工具类

JDK以及特别常见的辅助库(例如Guava和Apache Commons)充满了实用程序类。 它们的名称通常是为其提供方法的接口的复数形式,例如Collections或Sets 。 它们存在的主要原因是那些实用程序方法在发布后不能添加到原始接口中。 使用默认方法,这成为可能。

现在,所有将接口实例作为参数的静态方法都可以转换为接口上的默认方法。 例如,查看静态Collections.sort(List) ( link ),从Java 8开始,它静态地委派给新实例默认方法List.sort(Comparator) ( link )。 我的帖子中给出了另一个示例, 说明如何使用默认方法来改进装饰器模式 。 其他不带参数的实用程序方法(通常是生成器)现在可以成为接口上的静态默认方法。

虽然可以在代码库中删除所有与接口相关的实用程序类,但建议不要这样做。 界面的可用性和内聚性应该仍然是主要优先事项,而不是在其中填充所有可想象的功能。 我的猜测是,只有将这些方法中最通用的方法移到接口上,而在一个(或多个?)实用工具类中可以保留更多晦涩的操作,这才有意义。 (或完全删除它们 ,如果您愿意的话。)

分类

在对新Javadoc标签的争论中,Brian Goetz对迄今为止已引入JDK(格式化我的格式)的默认方法进行了弱分类:

1.可选方法
这是默认实现几乎不符合的情况,例如Iterator中的以下内容:
default void remove() {throw new UnsupportedOperationException("remove");
}

它遵守合同,因为合同显然是薄弱的,但是任何关心删除的类肯定会覆盖它。

2.具有合理默认值的方法,但可能会被足够关注的实现所覆盖
例如,再次从Iterator中进行:
default void forEach(Consumer<? super E> consumer) {while (hasNext())consumer.accept(next());
}

对于大多数实现来说,这种实现是完美的,但是如果某些类(例如ArrayList )的维护者有足够的动力去做的话,可能会有做得更好的机会。 Map上的新方法(例如putIfAbsent )也在此存储桶中。

3.几乎没有人会覆盖它们的方法
如谓词中的此方法:
default Predicate<T> and(Predicate<? super T> p) {Objects.requireNonNull(p);return (T t) -> test(t) && p.test(t);
}

Brian Goetz – 2013年1月31日

我将此分类称为“弱”分类,因为它自然缺乏关于在何处放置方法的硬性规定。 但是,这并没有使它无用。 恰恰相反,我认为这对交流它们有很大帮助,在阅读或编写默认方法时要牢记一件好事。

文献资料

请注意,默认方法是引入新的(非正式)Javadoc标记@apiNote@implSpec@implNote的主要原因 。 JDK经常使用它们,因此了解它们的含义很重要。 了解它们的一个好方法是阅读我的上一篇文章 (平稳,对吗?),其中详细介绍了它们。

继承与建立类

继承的不同方面以及如何使用它来构建类经常在关于默认方法的讨论中出现。 让我们仔细看看它们,看看它们与新语言功能的关系。

多重继承-什么?

通过继承,一个类型可以假定另一个类型的特征。 存在三种特征:

  • 类型 ,即通过子类型化类型另一种类型
  • 行为 ,即一种类型继承方法,因此其行为与另一种类型相同
  • state ,即一个类型继承了定义另一个类型状态的变量

由于类是其父类的子类型,并且继承了所有方法和变量,因此类继承显然涵盖了所有这三个特征。 同时,一个类只能扩展另一个类,因此仅限于单一继承。

接口是不同的:一个类型可以继承许多接口,并成为每个接口的子类型。 因此,从第一天开始,Java就一直支持这种多重继承。

但是在Java 8之前,实现类仅继承了接口的类型。 是的,它也继承了合同,但没有继承其实际实施,因此它必须提供自己的行为。 使用默认方法时,此更改发生了变化,因此从Java的版本8开始,还支持行为的多重继承。

Java仍然没有提供明确的方法来继承多种类型的状态。 但是,使用恶意方法或虚拟字段模式可以通过默认方法实现类似的效果。 前者很危险,不应该使用,后者也有一些缺点(特别是在封装方面),应谨慎使用。

默认方法与混合和特质

在讨论默认方法时,有时会将它们与mixins和traits进行比较。 本文无法详细介绍这些内容,但将粗略介绍它们与具有默认方法的接口的区别。 (可以在StackOverflow上找到mixins和trait的有用比较。)

混合蛋白

Mixins允许继承其类型,行为和状态。 一个类型可以从多个mixin继承,从而提供所有三个特征的多重继承。 根据语言的不同,也许还可以在运行时将混合添加到单个实例。

由于具有默认方法的接口不允许状态的继承,因此显然它们不是mixin。

特质

与mixin相似,特征允许类型(和实例)从多个特征继承。 它们还继承了它们的类型和行为,但是与混合混合不同,常规特征没有定义自己的状态。

这使得特征类似于具有默认方法的接口。 概念仍然不同,但是这些差异并非完全无关紧要。 将来我可能会再次讨论并进行更详细的比较,但是在那之前,我将为您提供一些建议:

  • 如我们所见, 方法调用解析并不总是那么琐碎,这会使快速使用默认方法的不同接口的交互成为复杂性的负担。 性状通常以一种或另一种方式缓解此问题。
  • 特性允许Java不完全支持的某些操作。 请参阅有关特质的Wikipedia文章中的“选择操作”后的项目符号列表。
  • 论文“ Java 8中的面向特征的编程”探讨了使用默认方法的面向特征的编程风格,并遇到了一些问题。

因此,尽管具有默认方法的接口没有任何特征,但是相似性允许像以前那样以有限的方式使用它们。 这与专家组的设计目标一致, 该目标试图在不与原始目标冲突的地方适应这种用例,即界面的发展和易用性。

默认方法与抽象类

现在,接口可以提供行为,它们可以进入抽象类的领域,很快就会出现问题,可以在给定的情况下使用。

语言差异

首先让我们说明一下语言层面的一些差异:

尽管接口允许多重继承,但它们基本上在类构建的其他各个方面都达不到要求。 默认方法永远不会是最终方法,无法同步并且不能覆盖Object的方法。 它们始终是公共的,这严重限制了编写简短且可重用方法的能力。 此外,接口仍无法定义字段,因此每个状态更改都必须通过公共API进行。 为适应该用例而对API进行的更改通常会破坏封装。

尽管如此,仍然存在一些用例,其中这些差异无关紧要,并且两种方法在技术上都是可行的。

概念差异

然后是概念上的差异。 类定义什么什么,而接口通常定义什么可以做

抽象类是完全特殊的东西。 有效的Java项目18全面解释了为什么接口在定义具有多个子类型的类型时优于抽象类。 (而且这甚至没有考虑默认方法。)要点是:抽象类对于接口的骨架(即部分)实现有效,但如果没有匹配的接口就不应存在。

因此,当有效地将抽象类简化为低可见性,接口的基本实现时,默认方法是否也可以消除这种情况? 决定: 不! 实现接口几乎总是需要某些或所有默认方法所缺少的类构建工具。 而且,如果某些界面没有,那显然是一种特殊情况,这不会使您误入歧途。 (有关使用默认方法实现接口时可能发生的情况,请参见此早期文章 。)

更多连结

  • Lambda状态的最终版本(第10章介绍了默认方法)
  • 官方教程
  • 关于如何发展接口的官方教程
  • JavaCodeGeeks教程
  • DZone教程

反射

这篇文章应该都谈过一个需要了解的默认方法。 如果您不同意,请鸣叫 , 发邮件或发表评论。 批准和+1也可以接受。

翻译自: https://www.javacodegeeks.com/2015/02/everything-you-need-to-know-about-default-methods.html

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

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

相关文章

php 在函数里打开链接,JavaScript中打开链接的几种方法介绍

如何使用JavaScript打开链接&#xff1f;本篇文章我们就来看看使用JavaScript打开链接的几种方法介绍。我们先来看一段代码使用Javascript实现打开链接&#xff0c;需要在location.href中替换要打开的链接的URL。location.href http://www.php.cn/blog.html;通过上述代码我们可…

三代基因组测序技术原理简介

考虑到cnblog不适合基因组领域这种类型的文章&#xff0c; 最终&#xff0c;我自己开通了公众号&#xff1a;碱基矿工&#xff0c;欢迎感兴趣的同学关注&#xff01; 也可以关注我的知乎&#xff1a;https://www.zhihu.com/people/yellowtree/activities 2018年1月修改&#x…

Java并发教程–锁定:显式锁定

1.简介 在许多情况下&#xff0c;使用隐式锁定就足够了。 有时&#xff0c;我们将需要更复杂的功能。 在这种情况下&#xff0c; java.util.concurrent.locks包为我们提供了锁定对象。 当涉及到内存同步时&#xff0c;这些锁的内部机制与隐式锁相同。 区别在于显式锁提供了其他…

lda php,主题模型︱几款新主题模型——SentenceLDA、CopulaLDA、TWE简析与实现

[导读]百度最近开源了一个新的关于主题模型的项目。文档主题推断工具、语义匹配计算工具以及基于工业级语料训练的三种主题模型&#xff1a;LatentDirichlet Allocation(LDA)、SentenceLDA 和Topical Word Embedding(TWE)。一、Familia简介帮Familia&#xff0c;打个小广告~ F…

最短路径问题matlab作图,[MATLAB基础] matlab最短路径的输出问题,用了递归,但程序出错了,请大虾指点...

matlab最短路径的输出问题,用了递归,但程序出错了,请大虾指点 程序如下function shortestpath(w,p,q)c->;l;f;tsize(w);mt(1,1);azeros(m,m);vw;for k1:mfor i1:mfor j1:mb[w(i,j),w(i,k)w(k,j)];w(i,j)min(b);if w(i,j)~v(i,j)a(i,j)k;%a(i,j)10*a(i,j)k;vw;endendendendw;…

快速浏览JAX-RS请求与方法匹配

在本文中&#xff0c;我们来看一下JAX-RS中与资源方法匹配的HTTP请求 。 它是JAX-RS的最基本功能之一。 通常&#xff0c;使用JAX-RS API的开发人员不会接触&#xff08;或真正不需要知道&#xff09; 匹配过程的细节&#xff0c;请放心&#xff0c;由于我们的RESTful&#xff…

$stat php,php stat函数怎么用

php stat函数用于返回关于文件的信息&#xff0c;其语法是fstat(file)&#xff0c;参数file必需&#xff0c;指规定要检查的文件。php stat函数怎么用&#xff1f;定义和用法stat() 函数返回关于文件的信息。语法fstat(file)参数file必需。规定要检查的文件。说明获取由 file 指…

Apache Cassandra和低延迟应用程序

介绍 多年来&#xff0c; Grid Dynamics拥有许多与NoSQL相关的项目&#xff0c;尤其是Apache Cassandra。 在这篇文章中&#xff0c;我们要讨论一个给我们带来挑战的项目&#xff0c;而我们在该项目中试图回答的问题今天也仍然适用。 数字营销和在线广告在2012年很受欢迎&…

Java十大简单性能优化

关于“ web scale ”这个流行词有很多炒作&#xff0c;人们花了很多时间来重新组织他们的应用程序体系结构&#xff0c;以使其系统“规模化”。 但是什么是扩展&#xff0c;我们如何确保可以扩展&#xff1f; 缩放的不同方面 上面提到的炒作主要是关于扩展负载 &#xff0c;即…

php在苹果手机上传不了图片大小,ThinkPHP后台上传图片无默认尺寸解决方法

原标题&#xff1a;ThinkPHP后台上传图片无默认尺寸解决方法随着网站移动端的日益普及&#xff0c;对后台数据的兼容性要求也越来越高。后台数据涵盖范围不断扩大的同时&#xff0c;使得程序处理能力也必须逐步提高。使用ThinkPHP进行长沙网站开发时&#xff0c;后台kindeditor…

宝塔php gd库,宝塔面板安装 EasyImag – 一款最简单图床的安装体验

近日闲逛&#xff0c;发现了一款图床&#xff0c;一款开箱即食的简单图床程序。因为没有数据库所以安装起来也是异常简单&#xff0c;我们看看功能&#xff1a;支持设置图片质量支持仅登录后上传支持QQ截图&#xff0c;剪切板上传支持在线管理(增删改查)支持上传图片转换为指定…

批准Oracle IDM中的特定Web服务

关于Web服务端点的快速发布&#xff0c;OIM和SOA在与批准有关的场景中使用了Web服务端点- 基本内容&#xff0c;但对于初学者可能有用 。 Oracle IDM与SOA套件集成并利用其提供与批准相关的功能&#xff08;说实话&#xff0c;SOA相当丰富&#xff0c;并且也被用作Web服务连接…

Oracle15001,Oracle11gR2RAC环境DBCA创建数据库报错ORA-15055ORA-15001

在Oracle 11gR2 GridInfrastructure和Database软件安装完成之后&#xff0c;执行DBCA创建数据库到30%的时候报如下错误&#xff0c;点击OK后提示忽略并问题现象:在Oracle 11gR2 GridInfrastructure和Database软件安装完成之后&#xff0c;执行DBCA创建数据库到30%的时候报如下错…

针对新手的Java EE7和Maven项目–第8部分

第1部分 &#xff0c; 第2部分 &#xff0c; 第3部分 &#xff0c; 第4部分 &#xff0c; 第5部分 &#xff0c; 第6部分 &#xff0c; 第7部分 第8部分 自上一篇文章以来&#xff0c;这一系列教程已经有很长时间了。 是时候恢复并在我们的简单项目中添加新功能了。 正…

玩Weld-Probe –一站式查看CDI的所有方面

焊接3.0.0.Alpha4被释放 &#xff0c;而我一直坐在在DevConf.CZ一间会议室。 Jozef Hartinger&#xff08; jozefhartinger &#xff09;或多或少地在几分钟前告诉我有关此最新版本的新功能的信息。 有一个特别的功能真正引起了我的注意&#xff0c;它是新的焊接探针机制。 什…

linux cmake装在自己目录下,如何在Linux下安装cmake

全部展开OpenCV 2.2和更高版本需要使用Cmake生成生成文件&#xff0c;因此需要先安装cmake. 还有其他需要先安装cmake的软件1. 在Linux环境中打开Web浏览器&#xff0c;输入URL:mac cmake gui&#xff0c;找到最新版本的位置. 通常&#xff0c;发布了两个版本的开源软件: “源分…

Java Bootstrap:Dropwizard与Spring Boot

如何在尽可能短的时间内使准备就绪的Java应用程序投入生产&#xff1f; 我不是一个早起的人&#xff0c;所以有时需要一些时间才能启动“所有系统”提示。直到不久之前&#xff0c;这对于Java应用程序来说都是正确的&#xff0c;但是与发明贪睡功能不同闹钟&#xff0c;我们将在…

物理数据模型(PDM)-概念数据模型 (CDM)-面向对象模型 (OOM):适用于已经设计好数据库表结构了。...

步骤如下&#xff1a; 一、反向生成物理数据模型PDM 开发环境 PowerDesigner 15 ,SQL Server2005 &#xff08;1&#xff09;在开始逆向生成PDM图之前&#xff0c;需要为指定的数据库创建ODBC数据源。以Windows xp操作系统为例&#xff0c;选择“开始”/“运行”命令&#xff0…

MySQL作为Kubernetes服务,可从WildFly Pod访问

Kubernetes上使用Vagrant的Java EE 7和WildFly&#xff08;技术提示&#xff03;71&#xff09;介绍了如何在使用Kubernetes和Docker托管的WildFly上运行琐碎的Java EE 7应用程序。 Java EE 7应用程序是在世界范围内交付的动手实验室 。 它使用与WildFly捆绑在一起的内存数据库…

麒麟Linux启动目录,优麒麟目录结构介绍 系统入门必备

对于Linux爱好者来说&#xff0c;深入了解Linux文件目录结构的标准和每个目录的详细功能&#xff0c;对于我们用好Linux系统至关重要&#xff0c;下面就由小编给大家介绍下优麒麟系统的目录结构&#xff0c;PS: 同样适用于其他Linux发行版。查看系统的全部目录&#xff1a;* 在…