什么是默认方法,为什么要有默认方法

简单说,就是接口可以有实现方法,而且不需要实现类去实现其方法。只需在方法名前面加个default关键字即可。

为什么要引入这个特性?因为接口是个双刃剑:好处是面向抽象而不是面向具体编程,缺陷是当需要修改接口时需要修改全部实现该接口的类。例如java 8之前的集合框架没有foreach方法,通常能想到的解决办法是在JDK里给相关的接口添加新的方法及实现。然而,对于已经发布的版本,是没法在给接口添加新方法的同时不影响已有的实现。所以引进的默认方法。他们的目的是为了解决接口的修改与现有的实现不兼容的问题。

抽象类与带默认方法的接口对比

相同点:

  • 都是抽象类型;

  • 都可以有实现方法;

  • 实现类或者继承类都可以选择实现(覆盖)原有方法

不同点:

  • 抽象类无法被多重继承,接口可以;

  • 抽象类和接口所反映出的设计理念不同。其实抽象类表示的是”is-a”关系,接口表示的是”like-a”关系;

  • 接口中定义的变量默认是public static final 型,且必须给其初值,所以实现类中不能改变其值;抽象类中的变量默认是 friendly 型,其值可以在子类中重新定义,也可以重新赋值。

默认方法支持多重继承引入的冲突问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public interface ia {
default void f() {
System.out.println("ia");
}
}

public interface ib {
default void f() {
System.out.println("ib");
}
}

public class cc implements ia,ib{
/*
* 两个接口有同一个默认方法时必须覆盖该默认方法
* 可以指定调用哪个默认方法,或者另写实现
*/
@Override
public void f() {
ia.super.f();
}
}

new cc().f();
/* output: ia */


public interface id extends ib {
@Override
default void f() {
System.out.println("id");
}
}

public class cd implements id {}

new cd().f();
/* output: id */

具体调用的方法优先级:

  • 声明在类里面的方法优先于任何默认方法
  • 类中无声明时优先选取最具体的实现,即选取最直接的继承(实现)关系中的默认方法