Java 8接口的变化包括接口中的静态方法和默认方法。在Java 8之前,接口中只能有方法声明。但是从Java 8开始,我们可以在接口中使用默认方法和静态方法。
### Java 8 接口
接口设计一直是一项艰巨的工作,因为如果我们想在接口中添加其他方法,则将需要对所有实现类进行修改。随着接口的过时,实现它的类的数量可能会增加到无法扩展的程度。所以在设计应用程序时,很多时候都会提供一个基础实现类,然后在对其进行扩展并覆盖适用于应用程序的方法。
下面我就就来研究一下接口的默认方法和静态方法,以及Java 8引入它们的原因。
### Java接口默认方法
如何创建接口默认方法:在接口中的方法签名中使用`default`关键字。
如:
```java
public interface InterfaceDemo1 {
void func1();
default void log(String str) {
System.out.println("InterfaceDemo1 log:" + str);
}
}
```
上面代码中`log()`方法是`InterfaceDemo1`接口的一个默认方法,当一个类实现了InterfaceDemo1时候,就不需要再为该方法提供实现。这种方式我们只需要在接口中添加默认方法并实现,就能给类扩展新的方法。
假设还有一个接口:
```java
public interface InterfaceDemo2 {
void func2();
default void log(String str) {
System.out.println("InterfaceDemo2 log:" + str);
}
}
```
其中`InterfaceDemo2`和`InterfaceDemo1`具有相同方法签名的默认方法`log()`。如果一个类同时实现了InterfaceDemo1和InterfaceDemo2而且不提供`log()`方法的实现,则编译器无法决定选择哪个,此时编译器将抛出编译时错误。
这就类似于Java不允许我们继承多个类,因为它会导致`Diamond`问题,编译器无法决定要使用哪种超类方法。
因此,如果一个类同时实现上述两个接口,则必须为`log()`方法提供实现。
```java
public class DefaultMethodDemo implements InterfaceDemo1, InterfaceDemo2 {
@Override
public void func1() {
}
@Override
public void func2() {
}
@Override
public void log(String str) {
System.out.println("DefaultMethodDemo log:" + str);
}
}
```
**接口默认方法需要注意的几点**
- 接口默认方法将帮助我们扩展接口,而不必担心破坏实现类。
- 接口默认方法缩小了接口和抽象类之间的差异。
- 接口默认方法使得我们无需创建基类,由实现类自己选择覆盖哪个默认方法实现。
- 接口默认方法增强了Java 8中的Collections API以支持lambda表达式。
- 层次结构中的任何类都具有具有相同签名的方法,则默认方法将变得无关紧要。如默认方法不能覆盖`java.lang.Object`中的方法。因为Object是所有java类的基类,即使我们将Object类方法定义为接口中的默认方法,也将是无用的,因为类始终使用的是Object类方法。
### Java接口静态方法
Java接口静态方法与默认方法类似,不同之处在于我们无法在实现类中覆盖它们。如果实现类中的实现不佳,此功能可帮助我们避免不良结果。
下面是一个简单的示:
```java
public interface InterfaceDemo3 {
default void log(String str) {
if (!isEmpty(str)) {
System.out.println("InterfaceDemo3 log:" + str);
}
}
static boolean isEmpty(String str) {
System.out.println("InterfaceDemo3 default method parameter empty check");
return str == null ? true : "".equals(str) ? true : false;
}
}
```
下面是一个实现类:
```java
public class StaticMethodDemo implements InterfaceDemo3 {
public boolean isEmpty(String str) {
System.out.println("StaticMethodDemo default method parameter empty check");
return str == null ? true : false;
}
public static void main(String[] args) {
StaticMethodDemo demo = new StaticMethodDemo();
demo.log("");
demo.log("test");
}
}
```
运行结果输出如下:
```
InterfaceDemo3 default method parameter empty check
InterfaceDemo3 default method parameter empty check
InterfaceDemo3 log:test
```
如果将接口方法从静态设置为默认,则会得到以下输出:
```
StaticMethodDemo default method parameter empty check
InterfaceDemo3 log:
StaticMethodDemo default method parameter empty check
InterfaceDemo3 log:test
```
Java接口静态方法仅对接口方法可见,实例对象无法访问,但是我们可以采用`类名.方法()`的方式来调用,如:`InterfaceDemo3.isEmpty("test")`
**接口静态方法需要注意的几点**
- 接口静态方法是接口的一部分,实例对象无法直接访问。
- 接口静态方法非常适合提供有效的方法,例如null检查,集合排序等。
- 接口静态方法不允许被实现类覆盖,来提供安全性。
### Java函数式接口
最后补充介绍一下函数式接口。有且只有一个抽象方法的接口称为函数式接口。
Java 8引入了`@FunctionalInterface`注解来将接口标记为函数式接口。`@FunctionalInterface`注解是一种避免在函数式接口中意外添加抽象方法的工具,就像`@Override`用来检查重写父类或实现接口的方法的正确性,`@FunctionalInterface`可以用来检查该接口是否是正确的的函数式接口。当然也可以不添加,但是使用它是一个好习惯。
函数式接口是Java 8非常受欢迎的功能,因为它使我们能够使用lambda表达式实例化它们。`java.util.function`是新增的一个包,里面包含了一系列函数式接口,来作为lambda表达式和方法引用的参数类型。
后面我们会继续学习函数式接口和lambda表达式。

Java 8 之接口的静态方法和默认方法