谨慎使用Java8的默认方法

默认方法给JVM的指令集增加了一个非常不错的新特性。使用了默认方法之后,如果库中的接口增加了新的方法,实现了这个接口的用户类能够自动获得这个方法的默认实现。一旦用户想更新他的实现类的话,只需覆盖一下这个默认方法就可以了,取而代之的是一个在特定场景下更有意义的实现。更棒的是,用户可以在重写的方法里面调用接口的默认实现来增加一些额外的功能。

目前为止一切都还不错。然而,给现有的Java接口增加默认方法可能会导致代码的不兼容。看个例子就很容易能明白了。假设有一个库,它需要用户实现它的一个接口作为输入:

interface SimpleInput {
 void foo();
 void bar();
}

abstract class SimpleInputAdapter implements SimpleInput {
 @Override
 public void bar() {
 // some default behavior ...
 }
}

在Java 8以前,上述这种接口和一个对应的适配器类的组合在Java语言中是一种很常见的模式。类库的开发人员提供了一个适配器来减少库使用者的编码量。然而提供这个接口的目的其实是为了能实现某种类似多重继承的关系。

我们假设有一个用户使用了这个适配器:

class MyInput extends SimpleInputAdapter{
 @Override
 public void foo() {
 // do something ...
 }
 @Override
 public 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 {
 @Override
 public void foo() {
 // do something ...
 }
 @Override
 public void bar() {
 SimpleInput.super.foo();
 // do something additionally ...
 }
}

为了实现和原先那个类同样的功能,这里我们用到了Java 8的新语法来调用接口的默认方法。同样的,我们把myMethod的逻辑放到某个基类MyBase里面。可以捶捶肩膀放松下了。重构之后棒极了!

我们使用的这个库得到了很大的改进。然而,维护人员需要添加另一个接口来实现一些额外的功能。这个接口叫做CompexInput ,它继承了SimpleInput类,并增加了一个额外的方法。由于通常都认为默认方法是可以放心地添加的,因此维护人员重写了SimpleInput类的默认方法并添加了一些额外的动作来给用户提供一个更好的默认实现。毕竟使用适配器类的时候这个做法也十分常见:

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

这个新特性看起来非常不错,因此ThirdPartyBaseClass类的维护人员也决定使用这个库了。为了实现这个,他将ThirdPartyBaseClass类实现了ComplexInput接口。

但这样的话对MyInput类意味着什么?由于它继承了ThirdPartyBaseClass类,因此默认实现了ComplexInput接口,这样的话调用SimpleInput的默认方法就不合法了。结果就是,用户的代码最后无法通过编译。还有就是,现在已经彻底无法调用这个方法了,因为Java把这种调用间接父类的super-super方法认为是不合法的。你只能去调用ComplexInput接口的默认方法了。然而这首先需要你在MyInput类中显式的实现一下这个接口。对于这个库的用户而言,这些改动完全是意想不到的。

(注:简单点说其实就是:

interface A {
 default void test() {

 }
}

interface B extends A {
 default void test() {

 }
}

public class Test implements B {
 public void test() {
  B.super.test();
  //A.super.test(); 错误
 }

}

当然这么写的话是用户主动选择实现了B接口,而文中的例子由于引入了一个基类,因此由于库和基类中都进行了一个看似没有影响的改动,实际上却导致用户代码无法通过编译)

很奇怪的是,Java在运行时并没有对这个进行区分。JVM的校验器允许一个编译过的类进行SimpleInput::foo方法的调用,尽管加载的这个类继承了ThirdPartyBaseClass的更新版本后隐式地实现了ComplexInput接口。要怪只能怪编译器了。(注:编译器与运行时的行为不一致)

那我们从中学到了什么?简单地说,不要在另一个接口中重写原接口的默认方法。不要用另一个默认方法来重写它,也不要某个抽象方法来重写它。总而言之,使用默认方法时应当十分谨慎。虽然它们使得Java现有的集合库的接口更容易改进了,但它允许你在类的继承结构中进行方法调用,这本质上其实是增加了复杂性。在Java 7以前,你只需遍历线性的类层次结构看一下实际调用的代码就可以了。当你觉得的确需要的时候,再去使用默认方法。

以上就是针对为什么要慎用Java8的默认方法进行的详细解释,希望对大家的学习有所帮助。

(0)

相关推荐

  • Java8新特性之默认方法(default)浅析

    一.什么是默认方法,为什么要有默认方法 简单说,就是接口可以有实现方法,而且不需要实现类去实现其方法.只需在方法名前面加个default关键字即可. 为什么要有这个特性?首先,之前的接口是个双刃剑,好处是面向抽象而不是面向具体编程,缺陷是,当需要修改接口时候,需要修改全部实现该接口的类,目前的java 8之前的集合框架没有foreach方法,通常能想到的解决办法是在JDK里给相关的接口添加新的方法及实现.然而,对于已经发布的版本,是没法在给接口添加新方法的同时不影响已有的实现.所以引进的默认方法

  • Java Collections.sort()实现List排序的默认方法和自定义方法

    1.java提供的默认list排序方法 主要代码: List<String> list = new ArrayList();list.add("刘媛媛"); list.add("王硕"); list.add("李明"); list.add("刘迪"); list.add("刘布"); //升序 Collections.sort(list,Collator.getInstance(java.uti

  • Java8接口的默认方法

    Java8接口的默认方法 什么是默认方法,为什么要有默认方法? 简单说,就是接口可以有实现方法,而且不需要实现类去实现其方法.只需在方法名前面加个default关键字即可. 为什么要有这个特性?首先,之前的接口是个双刃剑,好处是面向抽象而不是面向具体编程,缺陷是,当需要修改接口时候,需要修改全部实现该接口的类,目前的 java 8之前的集合框架没有foreach方法,通常能想到的解决办法是在JDK里给相关的接口添加新的方法及实现.然而,对于已经发布的版本,是没法在给接口添加新方法的同时不影响已有

  • Default Methods实例解析

    Oracle官网是这样介绍默认方法的,使用默认方法,可以达到往接口里面增加新的功能,而且保持与老版本代码兼容,也就是原来的实现类可以不需要被动修改.所以,默认方法位置是在接口里面:默认方法具有实现,不会强制具体类来现.Java 8要充分利用Lambda,需要增强大量的类库,但是又希望做到兼容性,只能用默认方法这个大招. 默认方法 默认方法与普通的接口方法相比,最前面增加default关键字,参数列表之后接大括号,实现该方法,再后面没有分号. 如果翻看jdk源码的interface,会发现多了de

  • 谨慎使用Java8的默认方法

    默认方法给JVM的指令集增加了一个非常不错的新特性.使用了默认方法之后,如果库中的接口增加了新的方法,实现了这个接口的用户类能够自动获得这个方法的默认实现.一旦用户想更新他的实现类的话,只需覆盖一下这个默认方法就可以了,取而代之的是一个在特定场景下更有意义的实现.更棒的是,用户可以在重写的方法里面调用接口的默认实现来增加一些额外的功能. 目前为止一切都还不错.然而,给现有的Java接口增加默认方法可能会导致代码的不兼容.看个例子就很容易能明白了.假设有一个库,它需要用户实现它的一个接口作为输入:

  • Java8 接口默认方法和静态方法

    目录 1.默认接口方法 2.静态接口方法 3.两者的场景差异 前些日有同学问我Java接口中为什么要增加一个默认方法.在Java 8 中不单单增加了默认接口方法还增加了静态接口方法.今天就来聊聊这两个方法. 1.默认接口方法 java中的默认接口方法是这样定义的: public interface NewInterface { ​ void otherMethod(); default void doSomething() { System.out.println(" do something

  • 30分钟入门Java8之默认方法和静态接口方法学习

    前言 上一篇文章30分钟入门Java8之lambda表达式,我们学习了lambda表达式.现在继续Java8新语言特性的学习,今天,我们要学习的是默认方法和静态接口方法. 这一Java8的新语言特性,在Android N中也得到了支持.至于如何在Android开发中配置Java8的开发环境,请查看上一篇文章30分钟入门Java8之lambda表达式. 默认方法 默认方法让我们能给我们的软件库的接口增加新的方法,并且能保证对使用这个接口的老版本代码的兼容性. 下面通过一个简单的例子来深入理解下默认

  • Java8中的默认方法(面试者必看)

    背景 在Java8之前,定义在接口中的所有方法都需要在接口实现类中提供一个实现,如果接口的提供者需要升级接口,添加新的方法,那么所有的实现类都需要把这个新增的方法实现一遍,如果说所有的实现类能够自己控制的话,那么还能接受,但是现实情况是实现类可能不受自己控制.比如说Java中的集合框架中的List接口添加一个方法,那么Apache Commons这种框架就会很难受,必须修改所有实现了List的实现类 现在的接口有哪些不便 向已经发布的接口中添加新的方法是问题的根源,一旦接口发生变化,接口的实现者

  • Java8中新特性Optional、接口中默认方法和静态方法详解

    前言 毫无疑问,Java 8是Java自Java 5(发布于2004年)之后的最重要的版本.这个版本包含语言.编译器.库.工具和JVM等方面的十多个新特性. Java 8是Java的一个重大版本,有人认为,虽然这些新特性领Java开发人员十分期待,但同时也需要花不少精力去学习.下面本文就给大家详细介绍了Java8中新特性Optional.接口中默认方法和静态方法的相关内容,话不多说了,来一起看看详细的介绍吧. Optional Optional 类(java.util.Optional) 是一个

  • 一篇文章带你认识Java8接口的默认方法

    前言 Java8是Oracle于2014年3月发布的一个重要版本,其API在现存的接口上引入了非常多的新方法. 例如,Java8的List接口新增了sort方法.在Java8之前,则每个实现了List接口的类必须定义sort方法的实现,或者从父类中继承它的实现.想象一下,如果List接口的继承体系非常庞杂,那么整个集合框架的维护量有多么大! 为此,在Java8中引入了一种新的机制:接口支持申明带实现的方法. 默认方法 前文提到了Java8中List接口新增了sort方法,其源码如下: publi

  • Java8默认方法Default Methods原理及实例详解

    这篇文章主要介绍了Java8默认方法Default Methods原理及实例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 Java 8 引入了新的语言特性--默认方法(Default Methods). Default methods enable new functionality to be added to the interfaces of libraries and ensure binary compatibility wit

  • Java8新特性之接口中的默认方法和静态方法

    写在前面 在Java8之前的版本中,接口中只能声明常量和抽象方法,接口的实现类中必须实现接口中所有的抽象方法.而在Java8中,接口中可以声明默认方法和静态方法,本文,我们就一起探讨下接口中的默认方法和静态方法. 接口中的默认方法 Java 8中允许接口中包含具有具体实现的方法,该方法称为"默认方法",默认方法使用 default 关键字修饰 . 例如,我们可以定义一个接口MyFunction,其中,包含有一个默认方法getName,如下所示. public interface MyF

  • java8新特性之接口默认方法示例详解

    前言 JAVA8 已经发布很久,而且毫无疑问,java8 是自 java5(2004年发布)之后的最重要的版本.其中包括语言.编译器.库.工具和 JVM 等诸多方面的新特性.Java8 新特性列表如下: 接口默认方法 函数式接口 Lambda 表达式 方法引用 Stream Optional 类 Date API Base64 重复注解与类型注解 接口默认方法 1.什么是接口默认方法 从 Java8 开始,程序允许在接口中包含带有具体实现的方法,使用 default 修饰,这类方法就是默认方法.

随机推荐