第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. 使用继承扩展接口

通过继承,可以很容易在接口中增加方法声明,还可以在新接口中结合多个接口。这两种情况都可以得到新接口