1.能力
为了更好地理解接口的好处以及在哪里使用它们,我们需要谈谈一些更抽象的东西。
一个类通常模拟一个特定的对象。接口较少地对应于对象,而更多地对应于它们的能力或角色。

例如,汽车、自行车、摩托车和车轮之类的东西最好表示为类和对象。但他们的能力——比如“我可以骑”、“我可以载人”、“我可以站立”——更好地呈现为界面。这里有些例子:
代码 | 描述 |
---|---|
|
对应移动能力 |
|
对应被骑的能力 |
|
对应于运输东西的能力 |
|
类Wheel 可以移动 |
|
该类Car 可以移动、骑乘和运输东西 |
|
班级Skateboard 可以移动和骑乘 |
2. 角色
接口极大地简化了程序员的生活。通常,一个程序有数千个对象、数百个类,但只有几十个接口,即角色。角色很少,但是组合起来的方式很多(类)。
重点是您不必在每个类中编写代码来与其他每个类进行交互。您只需要与他们的角色(界面)进行交互。
想象你是一名宠物训练师。与您一起工作的每只宠物都可以拥有多种不同的能力。您与邻居就谁的宠物发出的噪音最大进行了友好的争论。要解决这个问题,您只需将所有可以“说话”的宠物排成一行,然后给它们下达命令:说话!
你不关心他们是什么动物,或者他们有什么其他能力。即使他们可以做三重空翻。在这个特定的时刻,您只对他们大声说话的能力感兴趣。这是它在代码中的样子:
代码 | 描述 |
---|---|
|
能力CanSpeak 。这个接口理解命令 to speak ,意味着它有相应的方法。 |
|
具有这种特征的动物。 为了便于理解,我们提供了英文的类名。这在 Java 中是允许的,但这是非常不受欢迎的。
|
|
我们如何给他们命令? |
当你的程序中的类数量达到数千个时,你将无法没有接口。不用描述几千个类的交互,只描述几十个接口的交互就够了——这大大简化了生活。
当与多态结合时,这种方法通常会取得巨大成功。
3.default
接口方法的实现
抽象类可以有变量和方法的实现,但不能有多重继承。接口不能有变量或方法的实现,但可以有多重继承。
情况如下表所示:
能力/财产 | 抽象类 | 接口 |
---|---|---|
变量 | ✔ | ✖ |
方法实现 | ✔ | ✖ |
多重继承 | ✖ | ✔ |
所以,一些程序员真的希望接口能够拥有方法实现。但是能够添加一个方法实现并不意味着永远都会添加一个。如果需要,请添加它。或者,如果你不这样做,那就不要。
此外,多重继承的问题主要是由变量引起的。无论如何,这就是他们的决定和所做的。从 JDK 8 开始,Java 引入了向接口添加方法实现的能力。
这是更新后的表格(适用于 JDK 8 及更高版本):
能力/财产 | 抽象类 | 接口 |
---|---|---|
变量 | ✔ | ✖ |
方法实现 | ✔ | ✔ |
多重继承 | ✖ | ✔ |
现在对于抽象类和接口,您可以声明带有或不带有实现的方法。这是个好消息!
在抽象类中,没有实现的方法必须以abstract
关键字开头。您无需在具有实现的方法之前添加任何内容。在接口中,情况正好相反。如果方法没有实现,则不应添加任何内容。但是如果有实现的话,那么就default
必须加上关键字。
为简单起见,我们在下表中提供此信息:
能力/财产 | 抽象类 | 接口 |
---|---|---|
没有实现的方法 | abstract |
– |
具有实现的方法 | – | default |
问题
使用具有方法的接口可以极大地简化大型类层次结构。例如,抽象InputStream
和OutputStream
类可以声明为接口!这让我们可以更频繁、更方便地使用它们。
但是世界上已经有数千万(数十亿?)Java 类。如果你开始改变标准库,那么你可能会破坏一些东西。喜欢一切!😛
为了不意外地破坏现有的程序和库,决定接口中的方法实现具有最低的继承优先级。
例如,如果一个接口继承了另一个具有方法的接口,并且第一个接口声明了相同的方法但没有实现,那么继承接口的方法实现将不会到达继承接口。例子:
interface Pet
{
default void meow()
{
System.out.println("Meow");
}
}
interface Cat extends Pet
{
void meow(); // Here we override the default implementation by omitting an implementation
}
class Tom implements Cat
{
}
代码将无法编译,因为该类Tom
未实现该meow()
方法。
GO TO FULL VERSION