Java 8 允许在接口中定义默认方法,这些方法有具体的方法体。默认方法使用 default 关键字修饰。这使得接口在添加新方法时,不会破坏实现该接口的现有类(在 Java8 之前,接口中只能放抽象方法,而且子类必须实现全部抽象方法,或者中间通过一个抽象类来实现接口,具体实现直接继承抽象类,也可以避一定程度上支持接口添加新方法,然后在抽象类提供新方法的空实现)。
实现类可以选择覆盖默认方法,也可以直接使用接口中提供的默认实现(是不是感觉有点像抽象类了)。这种机制增强了接口的扩展性,使得在不修改现有实现类的情况下,可以向接口添加新的功能。
默认方法的语法与普通接口方法类似,只是在方法声明前添加了 default 关键字,并且提供了方法体。就像这样:
public interface MyInterface { // 常规接口方法(抽象方法),没有方法体 void regularMethod(); // 默认方法,前面多了一个 default 关键字 default void defaultMethod() { // 默认方法的具体实现 System.out.println("This is a default method in the interface."); } }
假设我们有一个现有的接口 Animal,现在想要向其中添加一个新的默认方法 makeSound(),而不想破坏任何已经实现了 Animal 接口的类。如下:
(1)版本一:在 Animal 接口中仅仅定义了一个 eat() 方法,代码如下:
package com.hxstrive.jdk8.interfaces; public class InterfaceDemo1 { public interface Animal { // 普通抽象方法 void eat(); } public static void main(String[] args) { Animal animal = new Animal() { @Override public void eat() { System.out.println("The animal eats"); } }; animal.eat(); // 输出:The animal eats } }
(2)版本二:随着时间的过去,我们需要在 Animal 接口中新增 makeSound() 方法,在 Java8 之前,所有 Animal 实现类必须实现 makeSound() 方法,但 Java8 支持默认方法,因此所有实现类不需要修改任何代码,完全兼容。代码如下:
package com.hxstrive.jdk8.interfaces; public class InterfaceDemo2 { public interface Animal { // 普通抽象方法 void eat(); // 默认方法 default void makeSound() { System.out.println("The animal makes sound"); } } public static void main(String[] args) { Animal animal = new Animal() { @Override public void eat() { System.out.println("The animal eats"); } }; animal.eat(); // 输出:The animal eats animal.makeSound(); // 输出:The animal makes sound } }
在上面的示例中,匿名 Animal 类实现了 eat() 方法,但是并没有覆盖默认方法 makeSound()。
Java8 接口默认方法的应用场景:
(1)向后兼容性:当需要在接口中添加新功能时,默认方法允许我们这样做而不会破坏现有的实现。这对于库的维护者特别有用,因为他们可以在不破坏用户代码的情况下添加新功能。
(2)添加通用功能:默认方法可以用来添加一些通用的、与业务逻辑不紧密相关的功能到接口中,这样所有实现该接口的类都可以直接使用这些功能,而无需重新实现。
(3)Lambda 表达式和函数式接口:默认方法可以与 Lambda 表达式结合使用,使得接口更加灵活和强大。例如,Java8 的 Collection 接口添加了许多默认方法,如 forEach、removeIf 等,这使得使用Lambda 表达式遍历和处理集合变得更加简单。
(4)多继承的替代方案:在Java中,类只能继承自一个父类。默认方法可以被视为一种多继承的替代方案,允许我们从多个接口中继承方法。