接口


在抽象类中,抽象方法本质上是定义接口规范:即规定高层类的接口,从而保证所有子类都有相同的接口实现,这样,多态就能发挥出威力。

如果一个抽象类没有字段,所有方法全部都是抽象方法:

  1. abstract class Person {
  2. public abstract void run();
  3. public abstract String getName();
  4. }

就可以把该抽象类改写为接口:interface

在Java中,使用interface可以声明一个接口:

  1. interface Person {
  2. void run();
  3. String getName();
  4. }

所谓interface,就是比抽象类还要抽象的纯抽象接口,因为它连字段都不能有。因为接口定义的所有方法默认都是public abstract的,所以这两个修饰符不需要写出来(写不写效果都一样)。

当一个具体的class去实现一个interface时,需要使用implements关键字。举个例子:

  1. class Student implements Person {
  2. private String name;
  3. public Student(String name) {
  4. this.name = name;
  5. }
  6. @Override
  7. public void run() {
  8. System.out.println(this.name + " run");
  9. }
  10. @Override
  11. public String getName() {
  12. return this.name;
  13. }
  14. }

我们知道,在Java中,一个类只能继承自另一个类,不能从多个类继承。但是,一个类可以实现多个interface,例如:

  1. class Student implements Person, Hello { // 实现了两个interface
  2. ...
  3. }

术语

注意区分术语:

Java的接口特指interface的定义,表示一个接口类型和一组方法签名,而编程接口泛指接口规范,如方法签名,数据格式,网络协议等。

抽象类和接口的对比如下:

abstract classinterface
继承只能extends一个class可以implements多个interface
字段可以定义实例字段不能定义实例字段
抽象方法可以定义抽象方法可以定义抽象方法
非抽象方法可以定义非抽象方法可以定义default方法

接口继承

一个interface可以继承自另一个interfaceinterface继承自interface使用extends,它相当于扩展了接口的方法。例如:

  1. interface Hello {
  2. void hello();
  3. }
  4. interface Person extends Hello {
  5. void run();
  6. String getName();
  7. }

此时,Person接口继承自Hello接口,因此,Person接口现在实际上有3个抽象方法签名,其中一个来自继承的Hello接口。

继承关系

合理设计interfaceabstract class的继承关系,可以充分复用代码。一般来说,公共逻辑适合放在abstract class中,具体逻辑放到各个子类,而接口层次代表抽象程度。可以参考Java的集合类定义的一组接口、抽象类以及具体子类的继承关系:

  1. ┌───────────────┐
  2. Iterable
  3. └───────────────┘
  4. ┌───────────────────┐
  5. Object
  6. ┌───────────────┐ └───────────────────┘
  7. Collection
  8. └───────────────┘
  9. ┌───────────────────┐
  10. └──────────│AbstractCollection
  11. ┌───────────────┐ └───────────────────┘
  12. List
  13. └───────────────┘
  14. ┌───────────────────┐
  15. └──────────│ AbstractList
  16. └───────────────────┘
  17. ┌────────────┐ ┌────────────┐
  18. ArrayList LinkedList
  19. └────────────┘ └────────────┘

在使用的时候,实例化的对象永远只能是某个具体的子类,但总是通过接口去引用它,因为接口比抽象类更抽象:

  1. List list = new ArrayList(); // 用List接口引用具体子类的实例
  2. Collection coll = list; // 向上转型为Collection接口
  3. Iterable it = coll; // 向上转型为Iterable接口

default方法

在接口中,可以定义default方法。例如,把Person接口的run()方法改为default方法:

接口 - 图1

实现类可以不必覆写default方法。default方法的目的是,当我们需要给接口新增一个方法时,会涉及到修改全部子类。如果新增的是default方法,那么子类就不必全部修改,只需要在需要覆写的地方去覆写新增方法。

default方法和抽象类的普通方法是有所不同的。因为interface没有字段,default方法无法访问字段,而抽象类的普通方法可以访问实例字段。

练习

用接口给一个有工资收入和稿费收入的小伙伴算税。

接口 - 图2下载练习:用接口算税 (推荐使用IDE练习插件快速下载)

小结

Java的接口(interface)定义了纯抽象规范,一个类可以实现多个接口;

接口也是数据类型,适用于向上转型和向下转型;

接口的所有方法都是抽象方法,接口不能定义实例字段;

接口可以定义default方法(JDK>=1.8)。

读后有收获可以支付宝请作者喝咖啡:

接口 - 图3