为什么我们需要Java中的默认方法?

时间:2014-09-11 09:56:38

标签: java programming-languages java-8

我看看Java 8新闻与7相比,除了lambdas或新时间框架等非常有趣的东西之外,我发现引入了一个新功能(?):默认方法

我在this article中找到了以下示例:

public interface Math {

    int add(int a, int b);

    default int multiply(int a, int b) {
        return a * b;
    }
}

对我来说似乎很奇怪。 上面的代码看起来像abstract类,带有实现的方法。那么,为什么要在接口中引入默认方法呢?这种方法的实际优势是什么?

在同一篇文章中,我读到了这个解释:

  

为什么要在接口中添加方法?我们之所以这是因为接口与它们的实现类太紧密耦合了。即,不能在不破坏实现者类的情况下在接口中添加方法。在接口中添加方法后,其所有实现的类必须声明此新方法的方法体。

这根本不能说服我。恕我直言我相信,当一个类实现一个接口时,必须为其中的每个方法声明方法体。这肯定是一个约束,但它也证实了它的“自然”和#34; (如果你明白我的意思......)

如果您对每个继承类都有共同的逻辑,那么您将把它放入实现abstract类中。

那么,默认方法的真正优势是什么?(看起来更像是一种解决方法,而不是新功能......)


更新我知道这种方法是为了向后兼容,但它仍然没有让我信服。接口表示类必须具有的行为。所以实现某个接口的类肯定会有这种行为。但如果有人可以随意更改界面,则此约束会被破坏。行为可以随时改变......我错了吗?

3 个答案:

答案 0 :(得分:12)

这是为了向后兼容。

如果您有其他人已实现的界面,那么如果您向界面添加新方法,则所有现有实施都将被破坏。

通过添加具有默认实现的新方法,您可以保持与现有实现的源代码兼容。

对于一个稍微简单/做作的例子,我们应该说你创建了一个库:

void drawSomething(Thing thing) {
}

interface Thing {
    Color getColor();
    Image getBackgroundImage();
}

现在你来做一个新版本的库,你想添加边框颜色的概念,这很容易添加到界面:

interface Thing {
    Color getColor();
    Color getBorderColor();
    Image getBackgroundImage();
}

但问题是,每个使用你的库的人都必须回过头来做过的每一个Skin实现,并添加这个新方法。

相反,如果您向getBorderColor提供了一个名为getColor的默认实施,那么所有内容"只是工作"。

答案 1 :(得分:6)

过去有很多方法在抽象接口上起作用。在Java 8之前,必须将它们放入与接口配对的附加类中,例如, CollectionsCollection

这种古老的方法既不比default方法更有说服力,也不实用。而不是list.sort()你不得不说Collections.sort(list)。这也意味着您在创建interface时必须做出基本决定,要么每个List实施都需要实施sort方法,要么您提供sort方法一个无法覆盖的实用程序类。

使用default方法,你可以同时拥有一个标准实现,List实现不需要自己实现,但如果具体实现有更有效的方法,仍然可以覆盖它知道它的内部结构,例如ArrayList.sort将其内部数组直接传递给Arrays.sort,跳过一些中间操作。

答案 2 :(得分:3)

假设您希望在声明的接口中添加新功能,直到Java 7,如果要在声明的接口中添加新方法,还必须在实现该方法的类中定义方法的实现接口

在java 8中,您可以添加包含实现的默认方法,并且所有子类将继承该方法。

编辑:(问题更新后)

  

接口表示类必须具有的行为

它仍然表示类必须具有的行为,您的混淆是如何定义行为。所有实现类都将继承默认方法,并且也可以自由编写自己的实现。考虑以下两种情况,

  1. 如果实现类不提供自己的实现并且只是继承默认方法。如果在接口中更改默认方法的行为,实现类将具有更新的行为,因为它们继承默认方法,因此它包含接口表示类必须具有的行为
  2. 如果实现类提供自己的默认方法版本,并且您将更改默认方法的行为(仅参数)。在这种情况下,实现类将具有两个重载方法,一个是先前定义的,另一个是继承的默认方法。如果你改变了完整的行为(也有返回类型的参数),它将在实现类时产生歧义,因为你不能通过改变返回类型来重载方法,并且实现将被破坏。再次,它持有一个接口代表一个类必须具有的行为
  3. 示例:

    在Java 8中添加了集合中的批量数据操作(参考:http://openjdk.java.net/jeps/107),以实现在forEach()接口中添加Iterable方法。在Iterable接口中添加抽象方法会破坏所有现有代码,因为每个类都必须实现该方法。

    解决问题,在Iterable界面中添加了默认forEach()方法,

    interface Iterable 
    { 
       default void forEach(Consumer<? super T> action) 
       { 
          for (T t : this) action.accept(t); 
       } 
    }
    

    参考:Java 8 : Default method in Interface