在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 设计者决定支持安全性和一致性。两个接口怎么冲突都没关系;如果至少一个接口提供了方法实现,则编译器会报错并且由程序员来解决歧义。
如果没有任何接口为共享方法提供默认实现,那么就没有冲突。实现类有两种选择:实现方法,或不实现方法并将该类声明为抽象类。
如果一个类继承一个父类且实现了一个接口,而且从父类和接口这两者继承了同样的方法,处理规则就比较容易。这种情况下,只关心父类的方法,直接忽视来自接口的默认方法。