在抽象类中,抽象方法本质上是定义接口规范:即规定高层类的接口,从而保证所有子类都有相同的接口实现,这样,多态就能发挥出威力。
如果一个抽象类没有字段,所有方法全部都是抽象方法:
1 2 3 4 abstract class Person { public abstract void run () ; public abstract String getName () ; }
就可以把该抽象类改写为接口:interface
。
在Java中,使用interface
可以声明一个接口:
1 2 3 4 interface Person { void run () ; String getName () ; }
所谓interface
,就是比抽象类还要抽象的纯抽象接口,因为它连字段都不能有。因为接口定义的所有方法默认都是public abstract
的,所以这两个修饰符不需要写出来(写不写效果都一样)。
当一个具体的class
去实现一个interface
时,需要使用implements
关键字。举个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Student implements Person { private String name; public Student (String name) { this .name = name; } @Override public void run () { System.out.println(this .name + " run" ); } @Override public String getName () { return this .name; } }
我们知道,在Java中,一个类只能继承自另一个类,不能从多个类继承。但是,一个类可以实现多个interface
,例如:
1 2 3 class Student implements Person , Hello { ... }
术语 注意区分术语:
Java的接口特指interface
的定义,表示一个接口类型和一组方法签名,而编程接口泛指接口规范,如方法签名,数据格式,网络协议等。
抽象类和接口的对比如下:
abstract class
interface
继承
只能extends一个class
可以implements多个interface
字段
可以定义实例字段
不能定义实例字段
抽象方法
可以定义抽象方法
可以定义抽象方法
非抽象方法
可以定义非抽象方法
可以定义default方法
接口继承 一个interface
可以继承自另一个interface
。interface
继承自interface
使用extends
,它相当于扩展了接口的方法。例如:
1 2 3 4 5 6 7 8 interface Hello { void hello () ; } interface Person extends Hello { void run () ; String getName () ; }
此时,Person
接口继承自Hello
接口,因此,Person
接口现在实际上有3个抽象方法签名,其中一个来自继承的Hello
接口。
继承关系 合理设计interface
和abstract class
的继承关系,可以充分复用代码。一般来说,公共逻辑适合放在abstract class
中,具体逻辑放到各个子类,而接口层次代表抽象程度。可以参考Java的集合类定义的一组接口、抽象类以及具体子类的继承关系:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 ┌───────────────┐ │ Iterable │ └───────────────┘ ▲ ┌───────────────────┐ │ │ Object │ ┌───────────────┐ └───────────────────┘ │ Collection │ ▲ └───────────────┘ │ ▲ ▲ ┌───────────────────┐ │ └──────────│AbstractCollection │ ┌───────────────┐ └───────────────────┘ │ List │ ▲ └───────────────┘ │ ▲ ┌───────────────────┐ └──────────│ AbstractList │ └───────────────────┘ ▲ ▲ │ │ │ │ ┌────────────┐ ┌────────────┐ │ ArrayList │ │ LinkedList │ └────────────┘ └────────────┘
在使用的时候,实例化的对象永远只能是某个具体的子类,但总是通过接口去引用它,因为接口比抽象类更抽象:
1 2 3 List list = new ArrayList(); Collection coll = list; Iterable it = coll;
default方法 在接口中,可以定义default
方法。例如,把Person
接口的run()
方法改为default
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public class Main { public static void main (String[] args) { Person p = new Student("Xiao Ming" ); p.run(); } } interface Person { String getName () ; default void run () { System.out.println(getName() + " run" ); } } class Student implements Person { private String name; public Student (String name) { this .name = name; } public String getName () { return this .name; } }
实现类可以不必覆写default
方法。default
方法的目的是,当我们需要给接口新增一个方法时,会涉及到修改全部子类。如果新增的是default
方法,那么子类就不必全部修改,只需要在需要覆写的地方去覆写新增方法。
default
方法和抽象类的普通方法是有所不同的。因为interface
没有字段,default
方法无法访问字段,而抽象类的普通方法可以访问实例字段。
练习 用接口给一个有工资收入和稿费收入的小伙伴算税。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public interface Income { double getIncome () ; double getTaxRate () ; default double getTax () { return getIncome() * getTaxRate(); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class BasicIncome implements Income { private double income; public BasicIncome (double income) { this .income = income; } @Override public double getIncome () { return this .income; } @Override public double getTaxRate () { return 0.1 ; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public class SalaryIncome implements Income { private double income; public SalaryIncome (double income) { this .income = income; } @Override public double getIncome () { if (income <= 5000 ) { return 0 ; } return (this .income - 5000 ); } @Override public double getTaxRate () { return 0.2 ; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class RoyaltyIncome implements Income { private double income; public RoyaltyIncome (double income) { this .income = income; } @Override public double getIncome () { return this .income; } @Override public double getTaxRate () { return 0.2 ; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class Main { public static void main (String[] args) { Income[] incomes = new Income[]{ new BasicIncome(3000 ), new SalaryIncome(7500 ), new RoyaltyIncome(12000 ) }; double total = 0 ; for (Income income : incomes) { total += income.getTax(); } System.out.println(total); } }
小结 Java的接口(interface)定义了纯抽象规范,一个类可以实现多个接口;
接口也是数据类型,适用于向上转型和向下转型;
接口的所有方法都是抽象方法,接口不能定义实例字段;
接口可以定义default
方法(JDK>=1.8)。