Java8新增特性,可以为接口中添加默认方法,实现这个接口的所有类都会继承这个方法,这样看起来,接口和类的界限就有点不明显了,同时也会带来多继承,菱形问题。这样设计的初衷是什么?

  重所周知,java8开始支持lambda表达式,可以把函数当做参数传递,最明显的lambda表达式应用场景莫过于对collection的每一个元素应用lambda。如果想为Collection实现lambda表达式:list.forEach(…); // 这就是lambda代码

  首先想到的是为Collection的父接口iterator添加抽象方法forEach()。然而,对于已经发布的版本,是没法在给接口添加新方法的同时不影响已有的实现。如果添加了,那么所有的iterator()实现类都要重写这个方法,如果只是jre自己的类库还好说,大量的第三方类库都使用到了java的优秀集合框架,如果都要重写,这是不合理的。

  因此,如果在Java 8里使用lambda的时候,因为向前兼容的原因而不能用于collection库,那有多糟糕啊。

  由于上述原因,引入了一个新的概念。虚拟扩展方法,也即通常说的defender方法, 现在可以将其加入到接口,这样可以提供声明的行为的默认实现。

  简单的说,Java的接口现在可以实现方法了。默认方法带来的好处是可以为接口添加新的默认方法,而不会破坏接口的实现。

  之前:Java接口纯粹是契约的集合,是一种程序设计的表达方式。从数据抽象的角度看,能够在不定义class的同时又可以定义type,将是程序设计中强大而有用的机制。Java接口就是这些纯粹的接口组成的数据抽象。Java接口只能够拥有抽象方法,它不涉及任何实现,也不能创建其对象(这一点和抽象类一致)。

  多重继承模型导致额外的复杂性,其中最著名的是钻石问题或者叫“讨嫌的菱形派生”(Dreadful Diamond onDerivation、DDD)。为什么Java接口能够避免多继承的复杂性,关键在于它仅仅包含abstract方法。然而从设计的角度看,Java接口放弃了多继承的内在/固有目标,而显得是一个权宜之计。

  现在:Java8之前,接口不能升级。因为在接口中添加一个方法,会导致老版本接口的所有实现类的中断。λ表达式作为核心出现,为了配合λ表达式,JDK中Collection库需要添加新的方法,如forEach(),stream()等,于是引入了默认方法(defender methods,Virtual extension methods)。它是库/框架设计的程序员的后悔药。对于以前的遗留代码,大家都不知道有这个新方法,既不会调用,也不会去实现,如同不存在;编写新代码的程序员可以将它视为保底的方法体。类型层次中任何符合override规则的方法,优先于默认方法,因为遗留代码可能正好有同样的方法存在。

  默认方法,理论上抹杀了Java接口与抽象类的本质区别——前者是契约的集合,后者是接口与实现的结合体。当然,语法上两者的差别和以前一样。这就需要程序员来自觉维护两者的本质区别,把默认方法作为库、框架向前兼容的手段

  默认方法的一个好处:多继承的著名的是钻石问题(The Diamond Problem )再次需要关注。因而使以前某些人认为的“为了解决多继承问题而引入接口机制”的说法变成明显的错误——以前也是错误的认识。

  默认方法的声明很简单,直接在接口中把方法声明为default,之后再写方法的实现即可。这样所有的实现类都会继承这个方法,问题是他带来的多继承问题如何解决?

  Iterable中的默认方法:

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

  

  下面是一个简单的默认方法实现:

public interface A {
default void foo(){
System.out.println("Calling A.foo()");
}
} public class Clazz implements A {
}
Clazz clazz = new Clazz();
clazz.foo(); // 调用A.foo()

  下面是一个多继承: 

public interface A {
default void foo(){
System.out.println("Calling A.foo()");
}
} public interface B {
default void foo(){
System.out.println("Calling B.foo()");
}
} public class Clazz implements A, B {
}

  这段代码不能编译 有以下原因:

  java:class Clazz 从types A到B给foo()继承了不相关的默认值

  为了修复这个,在Clazz里我们不得不手动解决通过重写冲突的方法,或者调用某个接口中的方法:

public class Clazz implements A, B {
public void foo(){}
} public class Clazz implements A, B {
public void foo(){
A.super.foo();
}
}

  钻石问题

  情况一:接口IA有子接口IB1、IB2,而类C implements IB1,IB2

package java8;
public class C implements IB1, IB2{
public static void main(String[] a) {
new C().m();
}
}   

  (1)如果仅仅A定义默认方法m(),执行IA的默认方法;
  (2)如果只有IB1、IB2其中一个override IA定义的m(),调用“最具体接口”默认方法;

  (3)如果IB1、IB2都override IA定义的m(),而C不提供自己的方法体,则编译错误!因为 “最具体接口”默认方法有两个。此时,C若Override m(),可以消去编译错误。

  (4)类C提供自己的方法体时,可以提供自己的代码,也可以指定调用C implements 的直接父接口的默认方法

  小结:多个接口提供默认方法,则“最具体接口”默认方法胜出,但是不得出现多个“最具体接口”。

  情况二:接口IA(或IB1)定义了默认方法m(),类A1相同的方法m(),类C是它们的子类型。

  如果类A1提供了实现,按照a simple rule: “the superclass always wins.”),父类的方法 被调用;

  如果类A1不提供实现,即A1中m()为抽象方法,仍然按照the superclass always wins.类C需要override m(),给出自己的实现。否则,要么 C声明为抽象类,要么编译错误。

  小结:父类有默认方法的等价物,则默认方法如同不存在。

  默认方法是对Java语言的有趣补充 – 你可以把他们看做是lambdas表达式和JDK库之间的桥梁。默认表达式的主要目标是使标准JDK接口得以进化,并且当我们最终开始使用Java 8的lambdas表达式时,提供给我们一个平滑的过渡体验。谁知道呢,也许将来我们会在API设计中看到更多的默认方法的应用。

  http://www.oschina.net/translate/java-8-explained-default-methods

最新文章

  1. 【转】关于FLASH中图文混排聊天框的小结
  2. ldconfig和ldd用法
  3. iOS设置导航栏标题
  4. CentOS系统下安装配置ftp服务
  5. PHP 类型判断和NULL,空值检查
  6. Convert Object to XML using LINQ
  7. mac终端命令大全全全全全全全全全
  8. Android Studio 查看密钥库证书指纹SHA1
  9. ueditor .net版本上传不了图片问题
  10. Listview点击事件
  11. Memcached常用命令及使用说明
  12. java.io.EOFException java.io.ObjectInputStream$PeekInputStream.readFully 错误
  13. Java核心技术II读书笔记(三)
  14. User experience
  15. Citrix 服务器虚拟化之十 Xenserver高可用性HA
  16. jQuery之Jcrop
  17. php基础知识(每天分享一些以前的笔记希望能帮助自学的朋友)
  18. HEVC,VP9,x264性能对比
  19. web性能测试
  20. 函数式编程之-定义能够支持Partial application的函数

热门文章

  1. Quartz教程
  2. PHP实现连接设备、通讯和发送命令的方法
  3. Sqlserver2012的数据导入到sqlserver2008或sqlserver2008R2
  4. tomcat与jboss 01
  5. js 实现图片上传 续
  6. mysql 删除死锁的事务
  7. Notepad++中的高级查找
  8. Display file information in the document window
  9. java普通类如何调用Spring的Service层?
  10. Mybatis中DAO层接口没有写实现类,Mapper中的方法和DAO接口方法是怎么绑定到一起的