一、Java8中接口的新功能
在Java8之前的版本,我们在接口中只能定义静态变量和抽象方法(只有方法定义,没有方法体)。
例如:
public interface I1{
// 静态变量
public static String NAME = "I1";
// 抽象方法
void sayHello();
}
在Java8,对接口增加了新的功能,静态方法和默认方法。
二、接口的默认方法
1.为什么要有这个特性?
首先,之前的接口是个双刃剑,好处是面向抽象而不是面向具体编程,缺陷是,当需要修改接口时候,需要修改全部实现该接口的类,目前的 Java 8 之前的集合框架没有 foreach 方法,通常能想到的解决办法是在JDK里给相关的接口添加新的方法及实现。然而,对于已经发布的版本,是没法在给接口添加新方法的同时不影响已有的实现。所以引进的默认方法。他们的目的是为了解决接口的修改与现有的实现不兼容的问题。
2.默认方法定义
默认方法就是接口可以有实现方法,而且不需要实现类去实现其方法。
默认方法的定义很简单,我们只需在接口的方法名前面加个 default 关键字即可实现默认方法。
例如:
public interface I1 {
default void hello() {
System.out.println("Hello from I1");
}
}
public class C1 implements I1 {
@ Override
public void hello() {
System.out.println("Hello from C1");
}
}
3.默认方法的特征
1)默认方法可以调用接口中的抽象方法
例如:
public interface I1 {
default void hello() {
System.out.println("Hello from I1");
}
}
public class C1 implements I1 {
@Override
public void hello() {
System.out.println("Hello from C1");
}
}
2)默认方法属于实例,子类可以根据需要覆盖默认方法
例如:
public interface I1 {
default void hello() {
System.out.println("Hello from I1");
}
}
public class C1 implements I1 {
@Override
public void hello() {
System.out.println("Hello from C1");
}
}
4.多个同名默认方法
在Java中,一个类可以实现多个接口,如果被实现的多个接口中存在相同签名的默认方法,类会根据下面三条规则来进行选择:
1)类中的方法优先级最高,类或父类中声明的方法的优先级高于任何声明为默认方法的优先级。
2)如果第一条无法判断,那么子接口的优先级更高:方法签名相同时,优先选择拥有最具体实现的默认方法的接口, 即如果B继承了A,那么B就比A更加具体。
3)最后,如果还是无法判断,继承了多个接口的类必须通过显式覆盖和调用期望的方法, 显式地选择使用哪一个默认方法的实现。
例子1:
类C1中没有声明方法hello, I2继承I1,依据规则2,C1的实例将会调用接口I2的默认方法。
public class Example1 {
public static void main(String[] args) {
// 输出 Hello from I2
new C1().hello();
}
public interface I1 {
default void hello() {
System.out.println("Hello from I1");
}
}
public interface I2 extends I1 {
default void hello() {
System.out.println("Hello from I2");
}
}
public static class C1 implements I1, I2 {
}
}
例子2:
C2,C1都没有声明hello方法,I2继承I1,基于规则2,C2的实例会调用I2接口的默认方法。
public class Example2 {
public static void main(String[] args) {
//输出Hello from I2
new C2().hello();
}
public interface I1 {
default void hello() {
System.out.println("Hello from I1");
}
}
public interface I2 extends I1 {
default void hello() {
System.out.println("Hello from I2");
}
}
public static class C1 implements I1{
}
public static class C2 extends C1 implements I1, I2 {
}
}
例子3:
C2继承了C1,但没有声明hello方法,C1覆盖了hello方法,根据规则1,C2的实例将会调用C1中的hello方法。
public class Example3 {
public static void main(String[] args) {
//输出 Hello from C1
new C2().hello();
}
public interface I1 {
default void hello() {
System.out.println("Hello from I1");
}
}
public interface I2 extends I1 {
default void hello() {
System.out.println("Hello from I2");
}
}
public static class C1 implements I1{
@Override
public void hello() {
System.out.println("Hello from C1");
}
}
public static class C2 extends C1 implements I1, I2 {
}
}
例子4:
C1同时实现了I1和I2, 由于I1和I2存在同样签名的hello方法,根据规则3,C1必须覆盖hello方法,可以在方法内显式选择调用I1或者I2的方法。
public class Example4 {
public static void main(String[] args) {
// 输出 Hello from I1
new C1().hello();
}
public interface I1 {
default void hello() {
System.out.println("Hello from I1");
}
}
public interface I2 {
default void hello() {
System.out.println("Hello from I2");
}
}
/**
* 由于编译器无法识别I1还是I2的实现更加具体,所以会抛出编译错误:”类 C1从类型 I1 和 I2 中继承了hello() 的不相关默认值“
* 要解决冲突,可以在C1中覆盖hello()方法并在方法内显式的选择调用I1还是I2的方法
*/ public static class C1 implements I1, I2 {
/**
* 显式地选择调用接口I1和 I2中的hello方法
*/ @Override
public void hello() {
I1.super.hello();
//I2.super.hello();
}
}
}
5.默认方法和抽象类的区别
1)一个类只能继承一个抽象类;但一个类可以实现多个带有默认方法接口。
2)抽象类有实例变量,而接口只能有类变量(即静态常量)。
三、接口的静态方法
Java 8 的另一个特性是接口可以声明静态方法。比如:
public interface I1{
// 静态变量
public static String NAME = "I1";
// 静态方法
static String getName(){
return NAME;
}
}
四、默认方法和静态方法的区别
1)默认方法属于类实例,静态方法属于类(接口);
2)默认方法可以被继承,静态方法不会被继承(但接口中的静态变量可以被继承);
3)默认方法可以访问接口的抽象方法,静态方法则不可以。
例如:
public class Example1 {
public static void main(String[] args) {
System.out.println("I1.NAME:" + I1.NAME);
System.out.println("I1.getName():" + I1.getName());
// 接口中的静态变量可以被子类继承
System.out.println("C1.Name:" + C1.NAME);
// 这里会抛出错误,提示静态方法无法给子类继承 'Static method may be invoked on containing interface class only'
//System.out.println("C1.getName():" + C1.getName());
// 默认方法可以被子类继承
new C1().hello();
}
public interface I1{
// 静态变量
public static String NAME = "I1";
// 默认方法
default void hello(){
System.out.println("Hello from I1");
//允许调用接口的抽象方法
sayHello();
}
// 抽象方法
void sayHello();
// 静态方法
static String getName(){
return NAME;
}
}
public static class C1 implements I1{
@Override
public void sayHello() {
System.out.println("I am C1.");
}
}
}
五、参考资料
Java 8 默认方法
一文带你认识Java8中接口的默认方法