我看看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
类中。
那么,默认方法的真正优势是什么?(看起来更像是一种解决方法,而不是新功能......)
更新我知道这种方法是为了向后兼容,但它仍然没有让我信服。接口表示类必须具有的行为。所以实现某个接口的类肯定会有这种行为。但如果有人可以随意更改界面,则此约束会被破坏。行为可以随时改变......我错了吗?
答案 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之前,必须将它们放入与接口配对的附加类中,例如, Collections
与Collection
。
这种古老的方法既不比default
方法更有说服力,也不实用。而不是list.sort()
你不得不说Collections.sort(list)
。这也意味着您在创建interface
时必须做出基本决定,要么每个List
实施都需要实施sort
方法,要么您提供sort
方法一个无法覆盖的实用程序类。
使用default
方法,你可以同时拥有一个标准实现,List
实现不需要自己实现,但如果具体实现有更有效的方法,仍然可以覆盖它知道它的内部结构,例如ArrayList.sort
将其内部数组直接传递给Arrays.sort
,跳过一些中间操作。
答案 2 :(得分:3)
假设您希望在声明的接口中添加新功能,直到Java 7,如果要在声明的接口中添加新方法,还必须在实现该方法的类中定义方法的实现接口
在java 8中,您可以添加包含实现的默认方法,并且所有子类将继承该方法。
编辑:(问题更新后)
接口表示类必须具有的行为
它仍然表示类必须具有的行为,您的混淆是如何定义行为。所有实现类都将继承默认方法,并且也可以自由编写自己的实现。考虑以下两种情况,
示例:
在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);
}
}