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

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

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

总览

我想我没有给这篇文章一个有意义的叙述。 原因在于,从本质上讲,这是一篇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,一经查实,立即删除!

相关文章

RAC+ASM在单机上恢复的过程

IT168技术文档】网上也有类似的例子&#xff0c;但不尽详细&#xff0c;也没多花心思解释流程及原因&#xff0c;可能对初试者不太解惑。有感于此&#xff0c;故认真整理了恢复步骤即操作流程&#xff0c;望有助于同行朋友。 环境&#xff1a;正式环境是IBM4IBM5(RAC)&#xf…

php event_base_new,php event拓展基本使用

php event拓展基本使用2019-02-04 22:02:28(527)1. 确定io复用类型(select/poll/epoll)php$cfg new EventConfig();$cfg->avoidMethod("select");$cfg->avoidMethod("epoll");$base new EventBase($cfg);$sig Event::signal($base, SIGTERM, func…

OSGi服务测试助手:ServiceRegistrationRule

OSGi服务测试可以有效避免与悬挂的服务引用有关的问题。 就像我在写简单服务贡献验证中所承诺的那样&#xff0c;这次我引入了一个JUnit规则 &#xff0c;该规则有助于测试组件之间的交互。 OSGi服务测试组件交互 假设我们有一个服务&#xff0c;该服务通知根据whiteboard-pat…

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;这些锁的内部机制与隐式锁相同。 区别在于显式锁提供了其他…

POJ 1422 Air Raid (最小路径覆盖)

题意 给定一个有向图&#xff0c;在这个图上的某些点上放伞兵&#xff0c;可以使伞兵可以走到图上所有的点。且每个点只被一个伞兵走一次。问至少放多少伞兵。 思路 裸的最小路径覆盖。 最小路径覆盖 【路径覆盖】在一个有向图G(V, E<u,v>)中&#xff0c;路径覆盖就是在图…

自编码网络粒子群matlab,粒子群算法优化BP

这是数据1999 1611 1746 1277 753 323 229 7.10561325 1064 1414 1026 430 191 153 24.54521125 839 1197 857 301 168 139 …

在非托管对象中使用Spring托管Bean

即使我们想使用现有的最佳和最新技术&#xff0c;我们也必须处理遗留代码。 想象一下&#xff0c;新代码是用Spring框架的最新技术编写的&#xff0c;而旧代码根本不是用Spring编写的。 然后在非托管Spring对象中使用Spring托管Bean是我们必须处理的模式之一。 遗留代码具有非托…

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

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

Spring项目中的Netflix Archaius属性

Archaius基础 Netflix Archaius是用于管理应用程序配置的库。 考虑一个属性文件“ sample.properties”&#xff0c;其中包含一个名为“ myprop”的属性&#xff1a; mypropmyprop_value_default这是使用Archaius加载文件的方式&#xff1a; ConfigurationManager.loadCascad…

linux进程之间传递句柄

参考网上的实现&#xff0c;进行了一点改进&#xff0c;本来想用FIFO实现的&#xff0c;后来发现不行&#xff0c;必须使用系统的sendmsg函数&#xff0c;代码格式有点乱&#xff0c;不过功能可以实现&#xff0c;改天有空整理一下~~ 讲究看哈~ #include <sys/types.h>#i…

php 中间代码,PHP内核中用户函数、内部函数和中间代码的转换

昨天和一朋友在邮件中讨论这样一个问题&#xff1a;zend_internal_function&#xff0c;zend_function&#xff0c;zend_op_array这三种结构是可以相互转化的&#xff0c;这三者的转化是如何进行的呢&#xff1f; 以此文&#xff0c;总结。在函数调用的执行代码中我们会看到这样…

Byteman –用于字节码操纵的瑞士军刀

我正在JBoss的许多社区中工作&#xff0c;有很多有趣的事情要谈论&#xff0c;以至于我自己无法将自己的每一分都缠住。 这就是为什么我非常感谢有机会不时地欢迎客座博客的主要原因。 今天是Jochen Mader&#xff0c;他是以代码为中心的书呆子群的一部分。 目前&#xff0c;他…

最短路径问题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 指…

在Java中调用祖父母方法:您不能

在文章保护的重点中&#xff0c;我详细介绍了“受保护”如何扩展“包私有”访问。 我在那儿写道&#xff1a; 你能做的是 覆盖子类中的方法或 使用关键字super调用parent方法。 通常&#xff0c;这实际上是您可以使用受保护的方法完成的所有操作。 &#xff08;请注意&…

Handler(2)

andriod提供了Handler 和 Looper 来满足线程间的通信。Handler先进先出原则。Looper类用来管理特定线程内对象之间的消息交换(MessageExchange)。1)Looper: 一个线程可以产生一个Looper对象&#xff0c;由它来管理此线程里的MessageQueue(消息队列)。 2)Handler: 你可以构造Han…

OSGi服务测试助手:ServiceCollector

OSGi服务对于基于松耦合组件的系统开发非常有用。 但是&#xff0c;松散的耦合可能使得难以识别与悬挂服务引用有关的问题。 因此&#xff0c;我们通常运行集成测试以确保运行时服务组件的可用性。 为了减少此类测试所需的样板&#xff0c;我编写了一个简单的实用程序类来获取…