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 。

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

可能绕行

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

通配符

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

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

专用接口,类,实例

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

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

容器适配器

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

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

算了!

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

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

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

反射

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

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

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

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

svn: 没有演进历程信息

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

  1. viewpager默认界面_使用默认方法的界面演变–第一部分:方法

    viewpager默认界面 几周前,我们详细研究了默认方法 -Java 8中引入的一项功能,该功能允许为接口方法提供实现,即方法主体,从而定义接口中的行为. 引入此功能是为了实现接口演进 . 在JDK ...

  2. java8新特性_乐字节-Java8新特性-接口默认方法

    总概 JAVA8 已经发布很久,而且毫无疑问,java8是自java5(2004年发布)之后的最重要的版本.其中包括语言.编译器.库.工具和JVM等诸多方面的新特性. Java8 新特性列表如下: 接 ...

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

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

  4. 【Java 面向对象】基础、Java程序的内存划分、嵌套类(内部类、静态嵌套类)、局部类、接口的升级问题(默认方法、静态方法)、instanceof

    面向对象 对象的内存 复杂对象的内存 对象数组的内存 思考:方法存储在哪里? Java程序的内存划分 this.super 注解(Annotation) 访问控制(Access Control) to ...

  5. 深入理解Java 8 Lambda(语言篇——lambda,方法引用,目标类型和默认方法)

    作者:Lucida 微博:@peng_gong 豆瓣:@figure9 原文链接:http://zh.lucida.me/blog/java-8-lambdas-insideout-language- ...

  6. java8 默认方法_Java 8的默认方法:可以做什么和不能做什么?

    java8 默认方法 什么是默认方法 在Java 8发行版中,您可以修改接口以添加新方法,以便该接口与实现该接口的类保持兼容. 万一您开发了一个库,该库将由基辅到纽约的几位程序员使用,那么这非常重要. ...

  7. 使用默认方法进行接口演化–第二部分:接口

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

  8. Java 8的默认方法:可以做什么和不能做什么?

    什么是默认方法 在Java 8发行版中,您可以修改接口以添加新方法,以便该接口与实现该接口的类保持兼容. 如果您要开发一个库,该库将由基辅到纽约的几位程序员使用,那么这非常重要. 在Java 8出现之 ...

  9. 《Java8实战》笔记(09):默认方法

    默认方法 本文的源码 实现接口的类必须为接口中定义的每个方法提供一个实现,或者从父类中继承它的实现.但是,一旦类库的设计者需要更新接口,向其中加入新的方法,这种方式就会出现问题.现实情况是,现存的实体 ...

最新文章

  1. Linux Ubuntu 自动登录
  2. 051 日志案例分析(PV,UV),以及动态分区
  3. DB2定时清理归档日志脚本
  4. 你还记得当初为什么进入IT行业吗?
  5. 人工智能:第二章 知识表示方法
  6. CF24D Broken robot | DP 高斯消元
  7. YumRepo Error: All mirror URLs are not using问题解决
  8. 简单几步即可判断Linux系统有无被DDOS攻击的方法
  9. [转]版本二写代码的小女孩
  10. 一个风骚的C语言操作
  11. Java EE 企业网站_基于jsp的企业网站系统-JavaEE实现企业网站系统 - java项目源码...
  12. Android BGradualProgress 多种渐变、直角or弧角、进度条、加载条
  13. 转换构造函数与类型构造函数与运算符重载函数
  14. 【Python】彩色图片转为灰度图(4行脚本搞定)
  15. linux weblogic部署项目路径,Weblogic部署web项目获取项目根目录为null
  16. 如何卸载 Adobe Creative Cloud 桌面应用程序
  17. OCR文字识别软件在线如何操作?
  18. 如何对自己定义的目标进行分解
  19. cout 和printf的区别,输出不同
  20. 【系统分析师之路】2016年系统分析师下午案例分析真题

热门文章

  1. P5110-块速递推【特征方程,分块】
  2. jzoj6803-NOIP2020.9.26模拟tom【构造】
  3. P1429-平面最近点对(加强版)【分治】
  4. UOJ#244-[UER#7]短路【贪心】
  5. nssl1150,jzoj5309-密室【分层建图,SPFA】
  6. P1351-联合权值【树形结构】
  7. 【DP】Bovine Genetics G(P7152)
  8. 【图论】最短路上的统计(ssl 1500)
  9. 25、jdbc操作数据库(2)
  10. Flowable学习笔记(一、入门)