默认方法
Java SE 7时代为一个已有的类库增加功能是非常困难的。具体的说,接口在发布之后就已经被定型,除非我们能够一次性更新所有该接口的实现,否则向接口添加方法就会破坏现有的接口实现。默认方法(之前被称为虚拟扩展方法或守护方法)的目标即是解决这个问题,使得接口在发布之后仍能被逐步演化。
这里给出一个例子,我们需要在标准集合API中增加针对lambda的方法。例如removeAll方法应该被泛化为接收一个函数式接口Predicate,但这个新的方法应该被放在哪里呢?我们无法直接在Collection接口上新增方法——不然就会破坏现有的Collection实现。我们倒是可以在Collections工具类中增加对应的静态方法,但这样就会把这个方法置于“二等公民”的境地。
简单的说,Java的接口现在可以实现方法了。默认方法带来的好处是可以为接口添加新的默认方法,而不会破坏接口的实现
最简单的例子
让我们看一个最简单的例子:一个接口A,Clazz类实现了接口A。
public interface A {
default void foo(){
System.out.println("Calling A.foo()");
}
}
public class Clazz implements A {
}
代码是可以编译的,即使Clazz类并没有实现foo()方法。在接口A中提供了foo()方法的默认实现。
使用这个例子的客户端代码:
Clazz clazz = new Clazz();
clazz.foo(); // 调用A.foo()
多重继承
如果一个接口中定义了一个默认的方法,而另外的父类中也定义一个同名的方法,那么调动的时候,该选择哪个呢?
- 选择父类中的方法。如果父类重点方法提供了默认的实现,则子类中具有相同名字和参数的方法会被忽略。
- 接口冲突。如果父类提供了一个默认的方法,子类中也提供了具有相同名称和参数的方法(不管该方法是否是默认的),那么必须通过覆盖该方法来覆盖。
代码示例
public interface vehicle {
default void print(){
System.out.println("我是一辆车!");
}
}
多个默认方法
一个接口有默认方法,考虑这样的情况,一个类实现了多个接口,且这些接口有相同的默认方法,以下实例说明了这种情况的解决方法:
public interface vehicle {
default void print(){
System.out.println("我是一辆车!");
}
}
public interface fourWheeler {
default void print(){
System.out.println("我是一辆四轮车!");
}
}
第一个解决方案是创建自己的默认方法,来覆盖重写接口的默认方法:
public class car implements vehicle, fourWheeler {
default void print(){
System.out.println("我是一辆四轮汽车!");
}
}
第二种解决方案可以使用 super 来调用指定接口的默认方法:
public class car implements vehicle, fourWheeler {
default void print(){
vehicle.super.print();
}
}
静态默认方法
除了默认方法,Java SE 8还在允许在接口中定义静态方法。这使得我们可以从接口直接调用和它相关的辅助方法(Helper method),而不是从其它的类中调用(之前这样的类往往以对应接口的复数命名,例如Collections)。
public interface MyInterf {
String m1();
default String m2() {
return "Hello default method!";
}
static String m3() {
return "Hello static method in Interface!";
}
}