tp3 默认模块 默认方法_您需要了解的有关默认方法的所有信息

tp3 默认模块 默认方法

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

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

总览

我想我没有给这篇文章一个有意义的叙述。 原因在于,从本质上讲,这是一篇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时,才使用接口X的默认方法(默认或抽象;请参见规则2 )。

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

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

解决冲突

规则#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和synchronized )。 参数总是相似的:这不是默认方法的目的 ,引入这些功能将导致更复杂且易于出错的语言规则和/或代码。

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

默认方法vs Mixins和特征

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

混合蛋白

Mixins允许继承其类型,行为和状态。 一个类型可以从多个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

tp3 默认模块 默认方法

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

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

相关文章

存储过程 锁定并发_Java并发教程–锁定:显式锁定

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

C语言 PK 各大编程语言

今天分享一篇关于C语言为何如此有魅力的文章&#xff0c;如果你还在学习哪门语言的路口抉择&#xff0c;建议可以认真看看~以下为CSDN译文&#xff1a;没有什么技术可以应用长达50年之久&#xff0c;除非它真的比大多数其他东西都要好用——对于一种计算机行业的技术来说尤其如…

在switch语句中,case后的标号只能是什么?

switch语句用于基于不同条件执行不同动作。语法格式&#xff1a;switch (变量表达式){case 常量1: 语句;break;case 常量2: 语句;break;case 常量3: 语句;break;...case 常量n: 语句;break;default: 语句;break;}switch语句是一个条件选择语句&#xff0c;找到相同的…

C语言——结构体链表,附完整示例

引用自身的结构体&#xff0c;一个结构体中有一个或多个成员的基类型就是本结构体类型时&#xff0c;说明这个结构体可以引用自己&#xff0c;所以称作引用自身的结构体。例如下面的结构体&#xff1a;struct link{ char ch; struct link *p} a;p是一个可以指向struct link类型…

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

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

C语言知识总结——宏,枚举

1、define宏定义以#号开头的都是编译预处理指令&#xff0c;它们不是C语言的成分&#xff0c;但是C程序离不开它们&#xff0c;#define用来定义一个宏&#xff0c;程序在预处理阶段将用define定义的来内容进行了替换。因此在程序运行时&#xff0c;常量表中并没有用define定义的…

C语言知识总结——共用体

union 共用体&#xff08;联合体&#xff09;在进行某些算法的C语言编程的时候&#xff0c;需要使几种不同类型的变量存放到同一段内存单元中。也就是使用覆盖技术&#xff0c;几个变量互相覆盖。这种几个不同的变量共同占用一段内存的结构&#xff0c;在C语言中 以关键字union…

jboss入门_JBoss Forge NetBeans集成–入门

jboss入门JBoss Forge是构建基于Maven的Java EE项目的最快方法。 因此&#xff0c;它已经具有了令人敬畏的功能&#xff0c;使您作为开发人员的生活更加轻松。 在大多数情况下&#xff0c;使用Forge的人们可能会对创建Web应用程序感兴趣。 有很多方法可以开始使用Forge基础知识…

这几道 C/C 题涉及你的知识盲区?

8个C语言面试题&#xff0c;涉及指针、运算、函数、内存&#xff0c;看看你能做出几个&#xff01;1.gets()函数问&#xff1a;请找出下面代码里的问题&#xff1a;#include int main(void) {char buff[10];memset(buff, 0, sizeof(buff));gets(buff); //gets不检查输入的字符…

logback redis_使用Spring Boot和Logback登录到Redis

logback redis在进行集中式日志记录时&#xff0c;例如使用Elasticsearch&#xff0c;Logstash和Kibana或Graylog2&#xff0c;您可以为Java应用程序提供多个选项。 您既可以编写标准的应用程序日志&#xff0c;也可以使用Logstash解析这些日志&#xff0c;这些日志既可以直接使…

C 与 C 的真正区别在哪里?

C 与 C 的真正区别在哪里&#xff1f;C是中餐厨师的菜刀&#xff0c;做啥菜就那一把刀&#xff0c;切菜切肉切鱼&#xff0c;都是这一把刀&#xff0c;刀工好的师傅&#xff0c;豆腐都能切成一朵花。无论你提什么概念&#xff0c;都能用指针给你做出来&#xff0c;如果不行&…

html 怎么置顶表格,表格(Table)表头固定,内容上滚【5个实例】

当表格往上滚动的时候&#xff0c;表头固定不动&#xff0c;这样可以让用户时刻看清每一列的数据含义&#xff0c;这是人性化的设计&#xff0c;充分考虑了用户使用体验。本文将通过5个实例&#xff0c;来介绍这种表格设计。用户可通过下载源码&#xff0c;直接应用于自己的项目…

C语言变量的定义包括变量存储类型和变量的什么?

C语言变量的定义包括变量存储类型和变量的名称。C语言定义变量的格式&#xff1a;“数据类型 变量名;”&#xff0c;“数据类型”表示想要存储什么类型的数据&#xff0c;“变量名”就是你想给这个变量起个什么名字&#xff0c;通常都是用字母。变量的定义定义变量的格式非常简…

C语言代码注释必须用/**/ , 你没看错~

事情是这样的&#xff0c;有人离职&#xff0c;公司调我补缺。那个系统一直有个工程师在维护&#xff0c;参与该系统的新人来了又走&#xff0c;他始终泰然自若。刚过去一个礼拜&#xff0c;我就心下窃吼&#xff1a;“坑爹啊!”&#xff0c;也彻底体会到什么叫---绝对的权威、…

html文档的基本类型,HTML(网页的文档类型介绍)

一个html文件的第一行代码通常就是用于声明网页文档类型&#xff0c;其格式是:这一行不是属于标签文档类型:可以理解为不同的html版本&#xff01;html4.0 或4.01版本基本固定&#xff0c;但又有分化:严格性:了用的标签和属性相对较少&#xff0c;但能兼容更多的浏览器。宽松型…

C语言源代码展示:常用转换函数实现原理

编程时经常用到进制转换、字符转换。比如软件界面输入的数字字符串&#xff0c;如何将字符串处理成数字呢&#xff1f;和大家分享一下。01字符串转十六进制代码实现&#xff1a;void StrToHex(char *pbDest, char *pbSrc, int nLen) {char h1,h2;char s1,s2;int i;for (i0; i …

html5 移动 优化,第四天:HTML5移动站优化技巧 摘自《10天学会移动站SEO》

现在大家基本上做手机网站都是做成HTML5的&#xff0c;因为现在智能手机等移动设备越来越多&#xff0c;几乎全部支持HTML5&#xff0c;那么给网站适配上HTML5的网站就很是必要了。以前的WML网站已经淘汰&#xff0c;而最新的方式就这种最好。我们这一节就重点讲一讲HTML5移动网…

c语言中switch的用法是什么?

c语言中switch的用法是&#xff1a;功能&#xff1a;switch语句是多分支选择语句.用来实现多分支选择结构.if语句只有两个分支可供选择,而实际问题中常常要用到多分支的选择.例如,学生成绩分类(90为"A"等,80-89分为B等,70-90分为C等......).当然这些都可以用嵌套的if…

C语言“fread”函数的用法?

C语言“fread”函数的用法为“size_tf read(void *buffer,size_t size,size_t count,FILE *stream)”&#xff0c;其作用是从一个文件流中读数据&#xff0c;读取count个元素&#xff0c;每个元素size字节。示例1#include #include #include int main(){ FILE *stream; c…

html怎么设置数据条的颜色,jQuery EasyUI 数据网格 – 条件设置行背景颜色 | 菜鸟教程...

jQuery EasyUI 数据网格 - 条件设置行背景颜色本教程将向您展示如何根据一些条件改变数据网格(datagrid)组件的行样式。当 listprice 值大于 50 时&#xff0c;我们将为该行设置不同的颜色。数据网格(datagrid)的 rowStyler 函数的设计目的是允许您自定义行样式。以下代码展示如…