Menu

Java 接口:定义契约与 implements

Java 接口是什么,如何定义和实现接口,default 方法和 static 方法,以及接口与抽象类有什么不同。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

接口就是契约

接口声明一个类能做什么,而不说明怎么做。它是一组方法签名,任何实现它的类都承诺会履行这些方法。这样做的意义在于解耦:代码可以面向接口类型编写,而不必关心背后是哪个具体的类。

Circle implements Shape 表示 Circle 必须定义 area 和 perimeter。一旦定义好,Circle 就是一个 Shape,可以存放在 Shape 类型的变量里。

多个类,一个类型

当好几个彼此无关的类实现同一个接口时,接口的威力就显现出来了。面向 Shape 编写的代码可以处理它们所有:

循环从来不问“这是 Circle 还是 Rectangle?”,它只是调用 area(),并信任这份契约。以后再加一个 Triangle,这里一行都不用改。

实现多个接口

一个类只能继承一个类,但可以实现任意数量的接口。Java 就是这样安全地实现行为上的“多重继承”的:

在 implements 后面列出这些接口,用逗号隔开。类必须满足所有接口的要求。

default 方法和 static 方法

从 Java 8 开始,接口可以自带方法体。default 方法给实现类提供一个现成的实现,实现类会免费继承它(也可以重写):

default 方法让接口可以演进:添加一个方法,而不会破坏所有现有的实现类。相比之下,static 接口方法的行为和其他静态成员一样:它是一个直接通过接口本身调用的工具方法,比如 Comparator.naturalOrder()。

常量

接口中的字段隐式地是 public static final 的:它们是常量,而不是实例状态:

interface Physics {
    double GRAVITY = 9.81;   // automatically public static final
}

传统上,接口不保存属于单个对象的状态;没有实例字段,正是接口区别于类的特点之一。

函数式接口

只有一个抽象方法的接口叫作函数式接口,你可以用一个 lambda 表达式来实现它,而不用写一整个类:

这是 Java 的 lambda 以及 java.util.function 中各种类型(Function、Predicate、Supplier 等)的基础。@FunctionalInterface 注解让意图一目了然,并让编译器强制执行“只有一个方法”的规则。

接口与抽象类

两者都让你面向抽象编程,那么什么时候该选哪个?

  • 接口:一种可以被无关的类共享的能力。Bird 和 Airplane 都可以是 Flyable。一个类可以实现多个接口。没有实例状态。
  • 抽象类:在关系密切的类之间共享状态和代码。Cat 和 Dog 都继承 Animal,从而继承共同的字段和部分实现的方法。一个类只能继承一个类。

一种常见的模式是把两者结合起来:接口定义契约,抽象类实现其中的样板部分,具体的子类只需要填写特定的细节。

下一篇:抽象类

接口只定义行为,不带状态。抽象类介于接口和完整的类之间:它既可以声明未实现的方法,又可以拥有字段和构造函数。这就是下一篇的内容。

常见问题

Java 中的接口是什么?

接口是一份契约:一组类承诺会提供的方法签名(以及常量)。它说明类能做什么,而不是怎么做。类用 implements 关键字实现接口,并且必须为接口声明的每个抽象方法提供方法体。接口让彼此无关的类可以通过一个共同的类型互换使用。

Java 中接口和抽象类有什么区别?

一个类可以实现多个接口,但只能继承一个(抽象)类。传统上,接口不保存实例状态,只声明行为;而抽象类可以有字段、构造函数和部分实现的逻辑。要定义一种能被多个无关的类共享的能力,就用接口;要在关系密切的子类之间共享状态和代码,就用抽象类。

Java 接口里可以有方法体吗?

可以,从 Java 8 开始。标记为 default 的接口方法提供一个方法体,实现类会继承它(也可以重写);static 接口方法提供工具性的行为,可以直接通过接口本身调用。普通的接口方法仍然是抽象的,只有签名,实现类必须定义它们。

Coddy 编程语言插图

和 Coddy 一起学编程

开始