第9章 接口
接口和抽象类提供了一种将接口与实现分离的更加结构化的方法。
这种机制在编程语言中不常见,例如 C++ 只对这种概念有间接的支持。而在 Java 中存在这些关键字,说明这些思想很重要,Java 为它们提供了直接支持。
1. 抽象类和方法
对于像 Instrument 那样的抽象类来说,它的对象几乎总是没有意义的。创建一个抽象类是为了通过通用接口操纵一系列类。因此,Instrument 只是表示接口,不是具体实现,所以创建一个 Instrument 的对象毫无意义,我们可能希望阻止用户这么做。通过让 Instrument 所有的方法产生错误,就可以达到这个目的,但是这么做会延迟到运行时才能得知错误信息,并且需要用户进行可靠、详尽的测试。最好能在编译时捕捉问题。
Java 提供了一个叫做抽象方法的机制,这个方法是不完整的:它只有声明没有方法体。下面是抽象方法的声明语法:
abstract void f();
包含抽象方法的类叫做抽象类。如果一个类包含一个或多个抽象方法,那么类本身也必须限定为抽象的,否则,编译器会报错。
如果一个抽象类是不完整的,当试图创建这个类的对象时,Java 会怎么做呢?它不会创建抽象类的对象,所以我们只会得到编译器的错误信息。
2. 接口创建
使用 interface 关键字创建接口 // interfaces/PureInterface.java // Interface only looked like this before Java 8 public interface PureInterface { int m1(); void m2(); double m3(); }
Java 8 中接口稍微有些变化,因为 Java 8 允许接口包含默认方法和静态方法。
接口同样可以包含属性,这些属性被隐式指明为 static 和 final。
使用 implements 关键字使一个类遵循某个特定接口(或一组接口),它表示:接口只是外形,现在我要说明它是如何工作的。除此之外,它看起来像继承。
Java 8 为关键字 default 增加了一个新的用途(之前只用于 switch 语句和注解中)。当在接口中使用它时,任何实现接口却没有定义方法的时候可以使用 default 创建的方法体。
// interfaces/InterfaceWithDefault.java interface InterfaceWithDefault { void firstMethod(); void secondMethod(); default void newMethod() { System.out.println("newMethod"); } } // interfaces/Implementation2.java public class Implementation2 implements InterfaceWithDefault { @Override public void firstMethod() { System.out.println("firstMethod"); } @Override public void secondMethod() { System.out.println("secondMethod") } public static void main(String[] args) { InterfaceWithDefault i = new Implementation2(); i.firstMethod(); i.secondMethod(); i.newMethod(); } }
3. 抽象类和接口
尤其是在 Java 8 引入 default 方法之后,选择用抽象类还是用接口变得更加令人困惑。下表做了明确的区分:。
有一条实际经验:尽可能地抽象。因此,更倾向使用接口而不是抽象类。只有当必要时才使用抽象类。除非必须使用,否则不要用接口和抽象类。大多数时候,普通类已经做得很好,如果不行的话,再移动到接口或抽象类中。
4. 使用继承扩展接口
通过继承,可以很容易在接口中增加方法声明,还可以在新接口中结合多个接口。这两种情况都可以得到新接口