导航
default
default是Java8新增的一个关键字,该关键字用于在接口中声明方法,使用default声明的方法称为默认方法。
默认方法
在Java8之前接口中声明的方法全部都是抽象方法,不可以有具体实现,实现接口的类需要重写接口中声明的每个方法。但是从Java8开始接口中声明的方法可以有具体实现,更准确的说是接口中的默认方法可以有具体实现。不仅如此,从Java8开始接口中可以定义静态方法。
那么为什么会出现默认方法呢?其主要原因是Java8中引入了Stream。
对集合而言,每种集合都需要有获取Stream对象的方法——stream()。按照设计理念stream()方法应该在集合根接口Collection中声明,然后让Collection的实现类重写该方法。然而集合体系很庞大,如果直接在Collection接口中声明stream()方法,那么所有的Collection实现类都要重写该方法,对现有的架构改动极大。
所以如果Collection接口中可以定义有具体实现的stream()方法就不会破坏现有的架构,Collection的实现类只需要直接继承过来就可以了。在JDK8的源码中,我们可以看到Collection接口中定义有默认方法stream():
public interface Collection<E> extends Iterable<E> {
default Stream<E> stream() { ... }
...
}
下面演示默认方法的使用:
public class TestDefault {
public static void main(String[] ar) {
// 用Lambda表达式表示函数式接口的一个具体实现
MyInterface myInterface = () -> 1;
MyInterface.getInfo(myInterface.size(), myInterface.isEmpty());
}
}
// 函数式接口
interface MyInterface {
// 抽象方法
int size();
// 默认犯法
default boolean isEmpty () {
return size() == 0;
}
// 静态方法
static void getInfo(int size, boolean isEmpty) {
System.out.println("size is " + size + ", empty: " + isEmpty);
}
}
打印结果如下:
size is 1, empty: false
MyInterface接口中存在三个方法:一个抽象方法size(),一个静态方法getInfo()以及一个默认方法isEmpty()。因此MyInterface是一个函数式接口,我们可以用Lambda表达式可以表示函数式接口的一个具体实现——myInterface。
myInterface从MyInterface接口继承了有具体实现的isEmpty()方法,可以不用重写isEmpty()方法就直接调用该方法。
默认方法与冲突
在Java中有一条亘久不变的定理:类只能单继承,接口可以多实现。
Java8之前由于接口中的方法全部都是抽象方法,一个类如果同时实现两个拥有相同方法签名的接口并不会出现冲突,实现类只需要重写该方法即可。但是在Java8中出现了默认方法,这样一来就有可能会出现Java一直在避免的多继承问题——一个类从多个地方(类或接口)继承了有相同方法签名的方法。
遇到这种情况时,我们需要遵循下面三条原则解决冲突:
- 一、类中方法优先级最高。类或父类中定义方法的优先级高于任何声明为默认方法的优先级。
- 二、第一条原则无法进行判断,子接口中声明的默认方法的优先级仅次于类中声明方法的优先级。
- 三、上述两条原则仍然无法判断,实现类必须显式重写方法或选择使用哪一个默认方法的实现。
类与接口方法冲突
public class TestDefault1 {
public static void main(String[] ar) {
new Class1().print();;
}
}
class ClassFu {
public void print() {
System.out.println("It's ClassFu.");
}
}
interface A1 {
default void print () {
System.out.println("It's A1.");
}
}
interface B1 {
default void print() {
System.out.println("It's B1.");
}
}
class Class1 extends ClassFu implements A1,B1{ }
打印结果如下:
It's ClassFu.
例子中Class1继承了ClassFu并实现了接口A1和B1,A1和B1两个接口都声明了默认方法print(),ClassFu类中也定义有print()方法。那么调用Class1对象的print()方法时应当按照第一条原则:类或父类中声明方法的优先级最高,打印结果符合预期。
父接口与子接口方法冲突
public class TestDefault2 {
public static void main(String[] ar) {
new Class2().print();
}
}
interface A2 {
default void print () {
System.out.println("It's A2.");
}
}
interface B2 extends A2{
@Override
default void print() {
System.out.println("It's B2.");
}
}
class Class2 implements B2,A2 {}
打印结果如下:
It's B2.
例子中Class2同时实现了接口A2和B2,且接口B2继承了A2并重写了A2中的默认方法print()。那么调用Class2对象的print()方法时应该按照第二条原则:子接口的默认方法优先级更高,打印结果符合我们的预期。
接口与接口方法冲突
public class TestDefault3 {
public static void main(String[] ar) {
new Class3().print();
}
}
interface A3 {
default void print () {
System.out.println("It's A3.");
}
}
interface B3 {
default void print() {
System.out.println("It's B3.");
}
}
class Class3 implements A3,B3 {
@Override
public void print() {
A3.super.print();
}
}
打印结果如下:
It's A3.
例子中Class3同时实现了接口A3和B3,A3和B3接口声明了有相同方法签名的默认方法print(),此时按照第三条原则:必须在类中重写默认方法。这里我们选择A3接口中的默认方法作为方法实现,调用Class3对象的print()方法的打印结果符合预期。