乍一看, 默认方法为Java虚拟机的指令集带来了一个很棒的新功能。 最后,库开发人员能够开发已建立的API,而不会对其用户代码造成不兼容性。 使用默认方法,当将新方法引入该接口时,任何实现库接口的用户类都会自动采用默认代码。 而且,一旦用户更新了他的实现类,他就可以使用对他的特定用例更有意义的东西来覆盖默认值。 更好的是,用户可以从重写的方法中调用接口的默认实现,并在其周围添加逻辑。

到目前为止,一切都很好。 但是,向已建立的接口添加默认方法会使Java代码无法编译。 在查看示例时,这是最容易理解的。 让我们假设一个库需要其接口之一的类作为输入:

interface SimpleInput {void foo();void bar();
}abstract class SimpleInputAdapter implements SimpleInput {@Overridepublic void bar() {// some default behavior ...}
}

在Java 8之前,接口和相应适配器类的上述组合是Java编程语言中相当普遍的模式。 图书馆供应商通常会提供适配器,以节省图书馆用户的键入时间。 但是,还额外提供了接口以允许多重继承的近似。

让我们进一步假设用户使用了此适配器:

class MyInput extends SimpleInputAdapter{@Overridepublic void foo() {// do something ...}@Overridepublic void bar() {super.bar();// do something additionally ...}
}

通过此实现,用户最终可以与库进行交互。 请注意,该实现如何覆盖bar方法以向默认实现添加一些功能。

那么,如果库迁移到Java 8,会发生什么呢? 首先,该库很可能会弃用适配器类,并将功能移至默认方法。 结果,该接口现在将如下所示:

interface SimpleInput {void foo();default void bar() {// some default behavior}
}

使用此新界面,用户可以更新其代码以适应默认方法,而不必使用适配器类。 使用接口而不是适配器类的最大好处是能够扩展除特定适配器之外的另一个类。 让我们付诸实践,并迁移MyInput类以使用默认方法。 因为我们现在可以扩展另一个类,所以让我们另外扩展一些第三方基类。 这个基类的作用在这里并不特别相关,因此让我们假设这对我们的用例有意义。

class MyInput extends ThirdPartyBaseClass implements SimpleInput {@Overridepublic void foo() {// do something ...}@Overridepublic void bar() {SimpleInput.super.foo();// do something additionally ... }
}

为了实现与原始类中类似的行为,我们利用Java 8的新语法来调用特定接口的默认方法。 另外,我们将myMethod的逻辑移到了一些基类MyBase 。 拍拍我们的肩膀。 很好的重构!

我们正在使用的库取得了巨大的成功。 但是,维护人员需要添加另一个接口以提供更多功能。 此接口表示一个CompexInput ,它使用其他方法扩展了SimpleInput 。 因为默认方法通常被认为可以安全添加 ,所以维护者还重写了SimpleInput的默认方法,并添加了一些行为以提供更好的默认值。 毕竟,在实现适配器类时,这样做很普遍:

interface ComplexInput extends SimpleInput {void qux();@Overridedefault void bar() {SimpleInput.super.bar(); // so complex, we need to do more ...}
}

这项新功能非常强大,以至ThirdPartyBaseClass的维护者决定也依赖此库。 为此,他为ThirdPartyLibrary实现了ComplexInput接口。

但这对MyInput类意味着什么? 由于通过扩展ThirdPartyBaseClass隐式实现ComplexInput ,调用SimpleInput的默认方法突然变得非法。 结果,用户的代码不再编译。 而且,由于Java认为此调用与调用间接超类的super-super方法一样是非法的,因此现在通常也禁止调用此方法。 相反,您可以调用默认方法
ComplexInput类。 但是,这需要您首先在MyInput显式实现此接口。 对于图书馆的用户来说,这种变化很有可能是出乎意料的!

奇怪的是,Java运行时没有进行这种区分。 JVM的验证程序将允许已编译的类调用SimpleInput::foo即使已加载的类在运行时通过扩展ThirdPartyBaseClass的更新版本隐式实现了ComplexClass 。 这里只有编译器抱怨。

但是我们从中学到什么呢? 简而言之,请确保不要在另一个接口中覆盖默认方法。 既不使用其他默认方法,也不使用抽象方法。 通常,要小心使用默认方法。 它们可以像Java的collection接口一样轻松地简化已建立的API的演变,但它们本身却很复杂,因为它们允许执行类型层次结构中的方法调用。 在Java 7之前,您只需要通过遍历线性类层次结构来查找实际调用的代码。 仅当您确实觉得有必要时才添加这种复杂性。

翻译自: https://www.javacodegeeks.com/2014/05/java-8-default-methods-can-break-your-users-code.html

Java 8默认方法可能会破坏您的(用户)代码相关推荐

  1. Java 8默认方法会破坏你的(用户的)代码

    Java 8的默认方法试图尝试更进一步简化Java API.不幸的是,这一最近的语言扩展带来了一系列复杂的规则,但只有少部分Java开发者意识到这一点.这篇文章告诉你为什么引入默认方法会破坏你的(用户 ...

  2. java默认代码地址_Java 8默认方法可能会破坏您的(用户)代码

    java默认代码地址 乍一看, 默认方法为Java虚拟机的指令集带来了一个很棒的新功能. 最后,库开发人员能够开发已建立的API,而不会对其用户代码造成不兼容性. 使用默认方法,当将新方法引入该接口时 ...

  3. java8 默认方法_如何不使用Java 8默认方法

    java8 默认方法 警告:一旦阅读,您将无法看不到它 我在上一篇博客文章中讨论了默认方法的多重继承,以及它们在编译和运行时的行为. 这周,我将研究如何使用默认方法进行真正的继承,实际上,默认方法并非 ...

  4. 如何不使用Java 8默认方法

    警告:一旦阅读,您将无法看不到它 我在上一篇博客文章中讨论了默认方法的多重继承,以及它们在编译和运行时的行为. 这周,我将研究如何使用默认方法进行真正的继承,实际上,默认方法并不是为这种方法而设计的. ...

  5. JAVA移慎_谨慎使用Java8的默认方法

    为什么要谨慎使用Java8的默认方法?本文给出了为什么要慎用Java8默认方法的原因,解释的很详细,感兴趣的朋友可以参考一下 默认方法给JVM的指令集增加了一个非常不错的新特性.使用了默认方法之后,如 ...

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

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

  7. java 二义性_Java接口默认方法带来的问题分析【二义性问题】

    本文实例分析了Java接口默认方法带来的问题.分享给大家供大家参考,具体如下: 一 点睛 Java 8中,如果一个类实现两个或多个接口,即"变相"的多继承,但是若其中两个接口都包含 ...

  8. java 代码 二义性是什么_Java接口默认方法带来的问题分析【二义性问题】

    本文实例分析了Java接口默认方法带来的问题.分享给大家供大家参考,具体如下: 一 点睛 Java 8中,如果一个类实现两个或多个接口,即"变相"的多继承,但是若其中两个接口都包含 ...

  9. java8接口写静态方法_Java 8接口更改–静态方法,默认方法

    java8接口写静态方法 Java 8 interface changes include static methods and default methods in interfaces. Prio ...

最新文章

  1. go语言中结构体与json
  2. Nagios使用check_mysql_health插件监控Mysql主机
  3. Git的使用_思维导图
  4. 畅购商城项目,面试问答,项目详解及全部代码
  5. 小学计算机兴趣班培训总结,小学生电脑制作兴趣班三大主题学习内容
  6. bochs镜像java模拟器_bochs镜像下载
  7. Mysql数据库repair table 修复表
  8. link_path_walk()分析
  9. 股票中什么是总负债同比增长率,有什么作用
  10. allegro中怎样制作和添加logo
  11. 神经网络算法有哪几种,神经网络有哪几种算法
  12. android屏幕旋转生命周期,Activity、Fragment生命周期---横竖屏切换的生命周期
  13. C#全自动工控屏上位机触摸源代码
  14. jquery 身份证工具类插件
  15. websocket网络断开之后重连
  16. [鲁林希] 学习动力篇
  17. 知识分享之Golang——go-i18n国际化组件
  18. ocr文字识别如何识别文字?
  19. java设计模式实战-(反射+策略模式)
  20. P5727 【深基5.例3】冰雹猜想

热门文章

  1. python 高维数据_Python数据分析入门|利用NumPy高效处理高维数据
  2. java反射机制的简单使用和优缺点
  3. 傅里叶变换公式_理解1维傅里叶变换
  4. map分组后取前10个_海关数据 | 图解前10个月外贸
  5. apache.camel_Apache Camel 3.1 –即将推出更多骆驼核心优化
  6. java编程学习方法_在线学习Java编程的最佳方法
  7. stripe pay_J2Pay –完整示例
  8. junit注释_通过此注释改善您的JUnit体验
  9. 不可变集合相比可变集合_简单的基准测试:不可变集合VS持久集合
  10. rest api_REST API的演变