在 Java SE 8 之前,接口中是不能提供方法实现的,但是 JDK8 提供了接口的默认方法和静态方法的支持。
# 默认方法
方法前加 default 关键字就可以提供默认实现,类实现接口时,可以继承接口的默认方法,也可以覆盖默认方法。
interface People { | |
default void eat(String name) { | |
System.out.println(name + " is eating."); | |
} | |
} |
抽象类也可以提供方法的默认实现,一个类可以同时继承一个抽象类和多个接口,如果抽象类和接口中存在相同的方法时会怎么样呢?
# 一个抽象类和多个接口
如果类继承了抽象类,采取类优先的原则,优先继承抽象类的方法。我们在编写类的时候会发现,必须先 extends 类再 implements 接口,否则工具会提示 extends 的错误。不论抽象类的方法有没有提供默认实现,类都会继承抽象类的方法,就算接口的方法提供了默认实现,也完全不关心。
接口 A,B:
interface A { | |
default void eat() { | |
System.out.println("A"); | |
} | |
} | |
interface B { | |
default void eat() { | |
System.out.println("B"); | |
} | |
} |
抽象类 C:
abstract class C { | |
public void eat() { | |
System.out.println("C"); | |
} | |
} |
测试类 Test:
public class Test extends C implements A, B { | |
public static void main(String[] args) { | |
Test test = new Java8Test(); | |
test.eat(); | |
} | |
} |
输出结果:
C
程序输出 C 说明 Test 类继承了抽象类 C 的 eat 方法的实现。
当抽象类 C 不提供 eat 方法的默认实现并且 Test 类不提供 eat 方法的重写时,编译器是不会放过 Test 类的,明明继承了 C 类,却不提供抽象方法的实现。编译器不会关心 Test 是否还实现了其他接口并且接口中有相同方法,一码归一码。
那么如果是抽象类提供了默认实现,接口没有提供呢?
我们让抽象类 C 实现 eat 方法,接口 A 只提供一个接口,类 Test 不重写 eat 方法。
接口 A,B:
interface A { | |
void eat(); | |
} | |
interface B { | |
default void eat() { | |
System.out.println("B"); | |
} | |
} |
抽象类 C:
abstract class C { | |
public void eat() { | |
System.out.println("C"); | |
} | |
} |
测试类 Test:
public class Test extends C implements A, B { | |
public static void main(String[] args) { | |
Test test = new Test(); | |
test.eat(); | |
} | |
} |
运行 main 方法,控制台打印出抽象类中 eat 方法的输出结果。
其实我们可以这么理解,编译器总要一步一步的编译,首先编译到 extends,检查所有的抽象方法是否被重写,有没被重写的方法就抛出编译错误,否则就继续编译。当编译到 implements 时,类 Test 已经是拥有抽象类 C 所有方法的实现了,即已经提供了接口 A 和 B 中相同方法的重写。
以上总结就是抽象类和接口的方法冲突时以抽象类为准就是了。如果抽象类不在了,两个接口方法冲突了,该听谁的呢?
# 多个接口
接口 A 提供方法默认实现,接口 B 只提供方法。
接口 A、B:
interface A { | |
default void eat() { | |
System.out.println("A"); | |
} | |
} | |
interface B { | |
default void eat() { | |
System.out.println("B"); | |
} | |
} |
测试类 Test:
public class Test implements A, B { | |
public static void main(String[] args) { | |
Test test = new Test(); | |
test.eat(); | |
} | |
} |
此时编译器提示,类 Test 从类型 A 和 B 中继承了 eat () 的不相关默认值。也就是说,当两个接口冲突时,编译器无法判断继承 A 还是继承 B 的方法实现。这时候,类 Test 就需要自己提供 eat () 方法的实现了。
# 静态方法
接口中也可以提供静态方法,使用 static 关键字。类不能继承接口的静态方法,所以也不存在覆盖静态方法。访问静态方法时通过接口访问,即 People.eat ("name")。
interface People { | |
static void eat(String name) { | |
System.out.println(name + " is eating."); | |
} | |
} |
可以测试一下,调用继承了 People 接口的类实例的 eat 方法,工具提示不能通过类实例访问静态成员。或者试着重写一个 eat 方法并加上 @Override 注解,工具提示 “Method does not override method from its superclass”。