许多开发语言都将函数表达式集成到了其集合库中。这样比循环方式所需的代码更少,而且更加容易理解。如下面的循环为例:java
for(int i = 0; i < list.size(); i++) System.out.println(list.get(i));
事实上有一种更好的方式。API开发人员能够提供一个forEach方法,用来将一个函数应用到集合的每一个元素上。下面是使用这种方式编写的一个简单调用:安全
package java8test; import java.util.Arrays; import java.util.List; public class T3 { public static void main(String[] args) { List<String> list = Arrays.asList("a","b","c","d","e","f"); //注意这一句代码 list.forEach(System.out::println); } }
若是集合库是彻底从新设计的,这样作不会有什么问题。可是,Java的集合库是许多年之前设计的,这就会带来一个问题。若是Collection接口添加了新的方法,例如forEach,那么每一个实现了Collection接口的自定义类就必须都实现该方法。这在java中是彻底没法接受的。ide
java设计者们但愿经过容许接口包含带有具体实现的方法(称为默认方法)来一劳永逸地解决这个问题。这些方法能够被安全地添加到已有的接口中。这里咱们将详细讲解默认方法。注:Java8中,forEach方法已经添加到Iterable接口中(它是Collection接口的父接口)。假设有以下接口:函数
interface Person { long getId(); //注意这里有一个default关键字 default String getName(){ return "John Q. Public"; } }
该接口有两个方法:一个抽象方法getId,以及一个默认方法getName。固然,实现Person接口的具体类必须实现getId方法,可是它能够选择保留getName的实现,或者重写它。spa
默认方法终结了之前的一种经典模式。即提供一个接口,以及一个实现接口的大多数或所有方法的抽象类,例如:Collection/AbstractCollection或WindowListener/WindowAdapter。如今你只须要在接口中实现那些方法。scala
若是一个接口中定义了一个默认方法,而另一个父类或接口中又定义了一个同名的方法,该选哪一个呢?像scala和C++等语言可能会有一套复杂的规则来解决这种二义性,可是幸运的是,Java中的规则要简单得多,以下所示:设计
选择父类中的方法。若是一个父类提供了具体的实现方法,那么接口中具备相同名称和参数的默认方法会被忽略。code
接口冲突。若是一个父接口提供了一个默认方法,而另外一个接口也提供了一个具备相同名称和参数类型的方法(无论该方法是不是默认方法),那么你必须经过覆盖该方法来解决冲突。 继承
咱们来详细理解一下第二条规则。假定另外一个接口也含有一个名为getName的方法:接口
interface Named{ default String getName(){ return getClass().getName() + "_" + hashCode(); } }
若是你编写了一个同时实现这两个接口的类,会发生什么事呢?
class Student implements Person,Named { ...... }
该类会继承由Person和Named接口同时提供的getName方法,可是这两个方法的实现并不一致。Java编译器会报告一个错误,并交由开发人员来解决这种冲突,而不会自动选择其中一个。对于这种状况,你只须要在Student类中提供一个getName方法,在该方法中再选择调用其中一个接口中的方法,以下所示:
interface Person{ long getId(); default String getName(){ return "John Q. Public"; } } interface Named{ default String getName(){ return getClass().getName() + "_" + hashCode(); } } class Student implements Person,Named{ @Override public long getId() { return 0; } public String getName(){ //注意这一句:Person.super.getName() return Person.super.getName(); } }
如今咱们假定Named接口没有提供getName方法的一个默认实现:
interface Named{ String getname(); }
若是这样,Student类能继承Person接口中的默认方法吗?也许这样说得过去,可是Java设计者们为了保持统一,仍是选择了与以前同样的处理方式。两个接口如何冲突不重要,只要有一个接口提供了实现,编译器就会报告一个错误,而开发人员必须手动解决这种冲突。注:固然,若是两个接口都没有为共享方法提供一个默认实现,那么咱们就又回到了Java8以前的状况,也就不存在什么冲突了。
如今咱们考虑这样一个类,它继承了父类并实现了某个接口,而这个父类和接口中都有一个同名的方法。例如,假设Person是一个类,而Student类的定义以下所示:
class Student extends Person implements Named {......}
在这种状况下,只有父类中的方法会起做用,接口中的任何默认方法都会被忽略。在这个例子中,无论Named接口中的getName方法是不是默认方法,Student都会继承Person类中的getName方法。这就是“类优先”的规则。“类优先”的规则能够保证Java7的兼容性。若是你在接口中添加了一个默认方法,它对Java8之前编写的代码不会产生任何影响。示例:
package java8test; public class T4 { public static void main(String[] args) { Student stu = new Student(); //类优先,会打印出:John Q. Public System.out.println(stu.getName()); } } class Person{ public long getId(){ return 100L; }; public String getName(){ return "John Q. Public"; } } interface Named{ default String getName(){ return getClass().getName() + "_" + hashCode(); } } class Student extends Person implements Named{}