java8新特性之接口变化

在java的早期版本中,所有的接口方法必须是抽象的(abstract)——也就是,没有方法体。现在,你可以添加两种有具体实现的方法:静态方法和默认方法。

静态方法

为什么接口不能有静态方法,技术上没有原因,但是,这不符合接口作为抽象规范的定义。这种思想现在变了。特别是,工厂方法在接口中非常有意义。在过去,将静态方法放在伴随类中一直是通常的做法,如 Collection/Collections 或 Path/Paths。现在,这种分割不再是必要的了。

默认方法

默认方法简介

你可以给接口的任意方法提供默认实现。你必须给这样的方法加上 default 修饰符标签。如:

public interface A{
    default boolean hasNext(){
        return true;
    }
}

实现该接口的类可以选择重写覆盖 hasNext() 方法或者选择继承默认的实现。
默认方法的一个重要用途是接口演化。例如 Collection 接口,假设一个类 B 实现了这个接口

public class B implements Collection

在 java8 中,Collection 接口添加了一个 stream 方法。假设 stream 不是默认方法,那么类 B 将无法编译,因为它没有实现新方法。给接口添加一个非默认方法会导致源代码无法兼容。

默认方法冲突

如果一个类实现了两个接口,其中一个接口有个默认方法,并且另一个接口也有名称和参数类型相同的方法(默认方法或非默认方法),此时,你必须解决冲突。

看个例子:有接口 Person,他有 getId 方法:

public interface Person{
    String getName();
    default int getId(){
        return 0;
    }    
}

同时有个接口 Identified,它也有一个 getId 这样的方法:

public interface Identified {
    default int getId(){
        //返回一个来源于对象的整数
        return Math.abs(hashCode());
    }
}

如果你创建一个实现这两个接口的类…

public class Employee implements Person, Identified {
    //会发生什么
}

该类继承了两个 getId 方法,java 编译器没有方法选择谁覆盖谁。编译器会报错,让你解决歧义。在 Employee 类中提供 getId 方法,并实现,或者委托其中一个冲突方法,就像这样:

public class Employee implements Person, Identified {
    public int getId(){
        return Identified.super.getId();
    }
}

如果 Identified 接口不提供 getId 方法的默认实现, Employee 类能继承 Person 接口的默认方法吗?乍一看,这也许是合理的,但是编译器如何知道 Psersion.getId 方法是否实际上做了 Identified.getId 接口被期望的事情? java 设计者决定支持安全性和一致性。两个接口怎么冲突都没关系;如果至少一个接口提供了方法实现,则编译器会报错并且由程序员来解决歧义。

如果没有任何接口为共享方法提供默认实现,那么就没有冲突。实现类有两种选择:实现方法,或不实现方法并将该类声明为抽象类。

如果一个类继承一个父类且实现了一个接口,而且从父类和接口这两者继承了同样的方法,处理规则就比较容易。这种情况下,只关心父类的方法,直接忽视来自接口的默认方法。