svn: 没有演进历程信息_使用默认方法的接口演进–第二部分:接口

svn: 没有演进历程信息

引入了默认方法以启用接口演进。 如果向后兼容性是不可替代的,则仅限于向接口添加新方法(这是它们在JDK中的唯一用法)。 但是,如果希望客户端更新其代码,则可以使用默认方法逐步演化接口而不会引起编译错误,从而使客户端有时间将其代码更新为新版本的接口。

这个小型系列的第一部分说明了默认实现如何允许在不破坏客户端代码的情况下添加,替换和删除方法。 我愚蠢地宣布,“以后的文章将探讨替换整个接口的方法”,同时也不会破坏客户端代码。

好吧,您现在正在阅读这篇文章,不幸的摘要是:

我无法使其工作。

为什么? 泛型。

到底为什么 你真的想知道吗 好吧,那么请继续阅读,但是这篇文章的其余部分实际上只是对我如何成为障碍的描述,因此不要期望太多。 (大激励,是吗?)

总览

在描述我尝试过的方法和失败的方法之前,我先定义要解决的问题。

问题陈述

这就是我们要做的:

假定您的代码库包含一个接口,您的客户端可以用所有可以想象的方式使用该接口:它们具有自己的实现,使用其实例调用您的代码,并且您的代码返回此类实例,当然他们将其用作参数的类型和返回值。

现在,您要实质性地更改接口:以无法用对单个方法的更改来表示的方式对其进行重命名,移动或修改。 (但是从提供一个版本到另一个版本的角度来看,这两个接口仍然是等效的。)

您可以执行此操作,发布包含更改的新版本,并告诉您的客户端修复其导致的编译错误。 如果他们的代码与您的代码高度耦合,那么他们可能必须在单独的分支中执行此操作以花一些时间在代码上,但这就是生活,对吗? 不过,您真是个好人,因此,与其要求费时的一天,不如让他们有机会随着时间的推移(例如,直到下一个版本)逐渐更改其代码,而没有任何编译错误。

(请注意,这是接下来所有内容的主要要求。我首先忽略了这是否是个好主意。我只是想看看自己能走多远。)

我认为甚至有机会实现这一目标的唯一方法是定义一个过渡阶段,在该阶段中,新旧版本的接口都将共存。 因此,我们真正需要的是一种通用的逐步方法,该方法如何将实现,调用者和声明从一个接口转移到另一个接口。

想法

在发布这篇文章时,我对它的工作方式有一个具体的想法。 基本上与我在方法中使用的方法相同。

不断发展的接口方法

使用默认方法添加,替换或删除接口的单个​​方法非常简单,通常包括三个步骤(在某些情况下更少):

  • 新版本:库的新版本发布,其中界面定义是过渡性的,并结合了旧的和新的所需轮廓。 默认方法可确保所有外部实现和调用仍然有效,并且在更新时不会出现编译错误。
  • 过渡:然后客户有时间从旧大纲过渡到新大纲。 同样,默认方法可确保适应的外部实现和调用有效,并且可以进行更改而不会产生编译错误。
  • 新版本:在新版本中,该库删除了旧轮廓的残差。 鉴于客户端明智地利用了自己的时间并进行了必要的更改,因此发布新版本不会导致编译错误。

如果您对这些步骤的详细说明感兴趣,可以阅读我的早期文章 。

改进界面

对于这种情况,这种方法似乎也很有意义,所以我坐下来进行了探讨。

如果整个接口发生变化,则要稍微复杂一点,因为在方法仅具有调用者和实现的地方,该接口也是一种类型,即可以在声明中使用。 这使得必须区分三种使用接口的方式:

  • 内部使用 ,您在其中拥有实现和使用接口的代码
  • 已发布的使用 ,您拥有实现,但客户端调用了代码
  • 外部使用 ,其中客户端拥有实现和使用接口的代码

起作用的部分采用与演化方法相同的方法:

  • 新版本:使用新界面发布新版本,以扩展旧版本。 让所有内部代码实现并使用新接口。 所有已发布的代码将使用旧接口声明参数类型,并使用新接口返回类型。 如果必须转换实例,则可以使用适配器来完成。 现在忽略参数化类型,此更改将不会导致客户端代码中的编译错误。
  • 过渡:发布后,客户端更改其代码。 从旧接口的实现(已更改为实现新接口的实现)和您已发布的代码返回的实例开始,它们可以开始声明新类型的实例,更新将它们传递给它们的方法的参数类型,等等。上。 如有必要,可以暂时使用适配器通过新接口与旧实例进行交互。
  • 新版本:发布一个删除旧界面的版本。

与不断发展的方法相同,新接口中的默认实现允许客户端代码停止明确实现旧接口,从而可以在第二个版本中将其删除。 此外,旧接口上的便捷asNew()方法可以调用适配器以使其自身适应新接口。

我掩饰了一些细节,但我希望你相信我,这是可行的。 现在让我们回到泛型…

障碍

提出的方法中的关键部分是已发布的代码。 它由您的客​​户调用,因此第一个发行版必须以兼容的方式对其进行更改。 并且由于所有内部代码都需要新接口,因此它必须从OldNew

没有泛型,它可能看起来像这样:

在已发布的代码中将“旧”转换为“新”

// in version 0
public Old doSomething(Old o) {// 'callToInternalCode' requires an 'Old'callToInternalCode(o);return o;
}// in version 1 the method still accepts 'Old' but returns 'New'
public New doSomething(Old o) {// 'callToInternalCode' now requires a 'New'New n = o.asNew();callToInternalCode(n);return n;
}

好的,到目前为止很好。 现在,让我们看看泛型的外观。

在已发布的代码中将“旧”转换为“新” –泛型

// in version 0
public Container<Old> doSomething(Container<Old> o) {// 'callToInternalCode' requires a 'Container<Old>'callToInternalCode(o);return o;
}// in version 1
// doesn't work because it breaks assignments of the return value
public Container<New> doSomething(Container<Old> o) {// 'callToInternalCode' requires a 'Container<New>'// but we can not hand an adapted version to 'callToInternalCode'// instead we must create a new containerNew nInstance = o.get().asNew();Container<New> n = Container.of(nInstance);callToInternalCode(n);return n;
}

因此,使用已发布的代码层从旧界面适应新界面通常不起作用,原因至少有两个:

  • 由于Java中泛型的不变性,返回值的所有分配都将中断:

    不变性打破分配
    Container<Old> old = // ...
    // works in version 0; breaks in version 1
    Container<Old> o = published.doSomething(old);
  • 不能将同一Container实例从已发布传递到内部代码。 这导致两个问题:
    • 创建一个新容器可能很困难或不可能。

该死的…

由华盛顿州交通运输部根据CC-BY-NC-ND 2.0发布。

发布时间由交通运输的华盛顿州部门在CC-BY-NC-ND 2.0 。

从一开始,我就感到仿制药会很麻烦-回想起来,这实际上很明显。 当涉及类型时,泛型怎么可能不是问题。 因此,也许我应该先尝试解决难题。

可能绕行

在将我的头撞在墙上一段时间之后,我仍然没有找到解决这个问题的通用方法。 但是我想出了一些可能有助于解决特殊情况的想法。

通配符

您可以检查已发布的内部代码是否充分利用了通配符(请记住PECS )。 您也可以建议客户如何使用它们。

根据情况,这可能会产生解决方案。

专用接口,类,实例

根据具体的代码,可以提供使用旧接口的已发布接口,类或实例的新版本。 如果可以通过让客户端选择使用依赖于旧接口的接口,类或实例还是依赖于新接口的接口,类或实例的方式来处理代码,则各个实现不必进行转换。

但这可能会将旧界面推回内部代码,而内部代码刚刚更新为仅使用新接口。 听起来也不好。

容器适配器

您可以在已发布的代码中为与旧接口一起使用的容器提供适配器。 这实际上将允许您在这些容器上调用asNew()

(出于一个不相关的原因,我目前正在为某些JDK集合进行此类转换。下一版本的LibFX将包含它们;如果您好奇,可以在GitHub上查看演示。)

算了!

这一切又是为了什么? 为了防止客户创建分支,在将所有内容合并回master之前花一些时间在那里修复问题吗? 算了!

在这一点上,这是我对此事的看法。 只要您只处理单个方法,接口的演变就很顺利,但是当您要替换整个接口时,这似乎会很痛苦。 因此,除非有充分的理由介绍所有这些复杂性,否则我将以困难的方式进行操作,然后让客户对其进行分类。 还是根本不做。

而且,如果您只是重命名或移动界面,无论如何,大部分甚至全部工作都可以通过简单的搜索替换来完成。

反射

我们重申了如何将默认方法用于发布,过渡和发布三部分的界面演化。 尽管这对单个方法有效,但我们发现它无法替换整个接口。 主要问题是参数类型的不变性使我们无法使用已发布的代码作为适应层。

即使我们看到了一些解决该问题的方法,也没有一个好的解决方案脱颖而出。 最后,看起来不值得麻烦。

我有事吗 还是整个想法愚蠢? 为什么不发表评论!

翻译自: https://www.javacodegeeks.com/2015/04/interface-evolution-with-default-methods-part-ii-interfaces.html

svn: 没有演进历程信息

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

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

相关文章

蓝桥杯7届c语言 c组答案,第七届蓝桥杯C语言C组-(自己懂的题目)

第七届蓝桥杯C语言C组-(自己懂的题目)表示刚刚查了成绩&#xff0c;省赛一等奖&#xff0c;有资格去北京了&#xff0c;然后写一下总结&#xff0c;先来写一下我懂的题目&#xff0c;毕竟我也是菜鸟&#xff0c;听说国赛比预赛难几个等级。。。第一题报纸页数X星球日报和我们地…

关于多线程的几道面试题

点击蓝字关注我们第一题&#xff1a;线程的基本概念、线程的基本状态及状态之间的关系&#xff1f;线程&#xff0c;有时称为轻量级进程&#xff0c;是CPU使用的基本单元&#xff1b;它由线程ID、程序计数器、寄存器集合和堆栈组成。它与属于同一进程的其他线程共享其代码段、数…

大牛谈嵌入式C语言的高级用法

点击蓝字关注我们内存管理我们需要知道——变量&#xff0c;其实是内存地址的一个抽像名字罢了。在静态编译的程序中&#xff0c;所有的变量名都会在编译时被转成内存地址。机器是不知道我们取的名字的&#xff0c;只知道地址。 内存的使用时程序设计中需要考虑的重要因素之一&…

博科光纤交换机java_带有光纤的可扩展,健壮和标准的Java Web服务

博科光纤交换机java这篇博客文章讨论了负载下的基准Web服务性能。 要了解有关Web服务性能理论的更多信息&#xff0c;请阅读利特尔定律&#xff0c;可伸缩性和容错 。 使用阻塞和异步IO对Web服务进行基准测试 Web应用程序&#xff08;或Web服务&#xff09;如何在负载下&#…

很棒的C语言入门笔记,推荐收藏!

点击蓝字关注我们c语言入门C语言一经出现就以其功能丰富、表达能力强、灵活方便、应用面广等特点迅速在全世界普及和推广。C语言不但执行效率高而且可移植性好&#xff0c;可以用来开发应用软件、驱动、操作系统等。C语言也是其它众多高级语言的鼻祖语言&#xff0c;所以说学习…

C语言的注释要注意几点

点击蓝字关注我们如果领导给你一个项目的源码让你阅读&#xff0c;并理解重构代码&#xff0c;但里面一句注释都没有&#xff0c;我想这肯定是之前同事“删库跑路”了。看一份源码什么很重要&#xff1f;除了各种代码规范之外&#xff0c;还有一个比较重要的就是注释。注释虽然…

java自动推断类型_推断:Facebook的新Java静态分析工具

java自动推断类型如何使用Facebook的Infer改善Java开发工作流程&#xff1f; 如果您与技术话题保持同步&#xff08;如果您正在阅读此博客&#xff0c;我想您会这样做&#xff09;&#xff0c;那么您可能听说过Facebook 刚刚向公众发布的新工具&#xff1a;推断。 由于它来自F…

android官方架构组件,Android 架构组件官方文档01——LifeCycle

使用生命周期感知组件处理生命周期支持生命周期的组件执行操作以响应另一个组件(例如Activity和fragment)的生命周期状态更改。这些组件可帮助您生成组织性更好&#xff0c;并且通常更轻量的代码&#xff0c;这些代码更易于维护。常见的模式是在Activity和fragment的生命周期方…

C语言的核心和灵魂

点击蓝字关注我们提起C语言大部分开发者很自然就会想到指针二字&#xff0c;没错&#xff0c;作为C的核心和灵魂&#xff0c;它的地位咱们就不再赘述了。今天我们想跟大家讲的是指针中的两个特有名词&#xff1a;“悬空指针”和“野指针”。悬空指针C语言中的指针可以指向一块内…

javaone_JavaOne 2015 –提交技巧和建议

javaone大家都知道JavaOne 。 感觉就像一直存在。 而且&#xff0c;即使我们跌宕起伏&#xff0c;而地理位置也不是我们想要的那样&#xff0c;旧金山也很昂贵&#xff0c;而且和。 这是有关各种Java的顶级会议。 今年又再次成为程序委员会&#xff08;“ Java&#xff0c;DevO…

C语言_结构体总结,附实例源码

点击蓝字关注我们当前文章介绍动态堆空间内存分配与释放&#xff0c;C语言结构体定义、初始化、赋值、结构体数组、结构体指针的相关知识点&#xff0c;最后通过一个学生管理系统综合练习结构体数组的使用。1. 动态内存管理C语言代码----->编译----->链接------>可执行…

四大C语言知识总结

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

jdbc select语句_SELECT语句使用JDBC和Hibernate批量获取

jdbc select语句介绍 现在&#xff0c;我已经介绍了Hibernate对INSERT &#xff0c; UPDATE和DELETE语句的批处理支持&#xff0c;是时候分析SELECT语句结果集的批量提取了。 JDBC ResultSet提供了一个客户端Proxy游标&#xff0c;用于获取当前语句的返回数据。 执行该语句后&…

看懂开源项目,你得熟悉这几个 C++11 新特性

点击蓝字关注我们C11 中增加了许多的新特性。在本文中&#xff0c;我们来聊一下 lambda 表达式&#xff0c;闭包&#xff0c;std::function以及std::bind。lambda 表达式C11 中新增了 lambda 表达式这一语言特性。lambda 表达式可以让我们快速和便捷的创建一个 “函数”。下面是…

5元素升级android6,【五元素ifive X.7】无障碍升级,ifveX详细升级固件教程,快为爱机升级吧。...

android系统的乐趣就是可以随意的刷机&#xff0c;所以拿到如此高配置的ifiveX也是想随时体验最新的android系统。而很多朋友可能只会用设备&#xff0c;刷机这种比较技术性的动作就不太会弄了。也就在这周&#xff0c;才帮同事刷RUU把已经无法启动的G12刷了回来。所以刷机还是…

昆仑通态复制的程序可以用吗_昆仑通态专题(七):MCGS组态软件的设备窗口...

点击上方蓝色字体&#xff0c;关注我们设备窗口是MCGS嵌入版组态软件系统的重要组成部分&#xff0c;在设备窗口中建立系统与外部硬件设备的连接关系&#xff0c;使系统能够从外部设备读取数据并控制外部设备的工作状态&#xff0c;实现对工业过程设备的实时监控与操作。01 设备…

嵌入式C语言程序调试和宏使用的技巧

点击蓝字关注我们01.调试相关的宏在Linux使用gcc编译程序的时候&#xff0c;对于调试的语句还具有一些特殊的语法。gcc编译的过程中&#xff0c;会生成一些宏&#xff0c;可以使用这些宏分别打印当前源文件的信息&#xff0c;主要内容是当前的文件、当前运行的函数和当前的程序…

matlab中欠定方程组超定方程组_《数值天气预报》:球坐标系中的基本方程组

人们是如何预报天气的&#xff1f;目前的预报方法主要有两种&#xff1a;一种是基于由各种探测资料绘制的天气图&#xff0c;结合历史资料进行分析预测&#xff1b;另一种是基于大气方程组&#xff0c;利用数值解法对其进行求解&#xff0c;从而得到未来时刻的大气状态。后者就…

浏览器总是跳转到缓存界面_跳转到企业缓存之前要考虑的事项

浏览器总是跳转到缓存界面介绍 关系数据库事务是ACID &#xff0c;强大的一致性模型简化了应用程序开发。 由于启用Hibernate缓存是一项配置 &#xff0c;因此&#xff0c;只要数据访问层开始出现性能问题&#xff0c;就转向缓存非常吸引人。 添加缓存层确实可以提高应用程序性…

CryptoTab 服务器_如何架设FTP服务器,如何架设FTP服务器,具体架设方法

FTP服务器&#xff0c;则是在互联网上提供存储空间的计算机&#xff0c;它们依照FTP协议提供服务。 FTP的全称是File Transfer Protocol(文件传输协议)。顾名思义&#xff0c;就是专门用来传输文件的协议。简单地说&#xff0c;支持FTP协议的服务器就是FTP服务器。那么&#xf…