接口(interface)是一个比类更加抽象的概念,没接触过面向对象编程(OOP)的人第一眼看代码通常都会懵逼。
如果说类是同一种类似事物的集合,那么接口就是拥有共同特征的一些东西的集合。这些东西可以不是同一种。
打个比方,鸟和飞机都会飞,但是它们显然不是同一种东西,鸟属于动物类的子类,飞机属于交通工具的子类,它们之间根本没任何继承关系,但是“会飞”就是个接口,可以描述它们之间的共性。
用代码描述就是这样:
interface Flyable{
void fly();
}
class Bird extends Animal implements Flyable{
public void fly() {
//扇翅膀
}
}
class Plane extends Traffic implements Flyable{
public void fly() {
//发动引擎
//助推
//滑行
//升空
}
}
interface可以看作一个全是抽象的类,里面只有public abstract方法,这很好理解:当你描述一个公共的特征或者一个行为时,如果它是不可见的(private)那就没有任何意义了。
为什么是abstract呢?用这个例子,“会飞”仅仅是描述了一种共性,但是具体这东西怎么飞,大家各有各的飞法,当然就只能抽象了。当你实现这个接口时,必须重写这个抽象的方法。
再举个例子,比如我家里有水果刀,可以切水果;有剪刀,可以剪纸;有开瓶器,可以开酒瓶。还有个瑞士军刀,可以做上面一切的事情。我应该怎么抽象这些东西呢?
如果不使用接口,我们就只能这样:
class Knives{
public void chopFruits() {
//切水果
}
}
class Scissors{
public void cutPaper() {
//剪纸
}
}
class Corkscrews{
public void openBottle() {
//开瓶盖
}
}
class SwissArmyKnives extends Knives{
@Override public void chopFruits() {
//重写切水果
}
}
public class House {
public static void main(String[] args) {
// TODO Auto-generated method stub
Knives knife = new Knives();
knife.chopFruits();
Scissors scissor = new Scissors();
scissor.cutPaper();
Corkscrews corkscrew = new Corkscrews();
corkscrew.openBottle();
Knives sak = new SwissArmyKnives();//多态
sak.chopFruits();
}
}
这样抽象,不觉得别扭么?假如我要修改一下剪纸这个方法传入的参数,我是不是两个地方都得改,假如忘记了改瑞士军刀的,是不是程序就挂了?要知道java不允许多继承,瑞士军刀不能既是刀子的子类又是剪刀的子类。
那么假如我抽象一个工具类,里面提供了“切水果”“剪纸”“开瓶盖”三个抽象方法,那么问题又来了,继承抽象类工具时,如果不把这三个方法都实现,就会报错!怎么办?
interface Chopable{
void chopFruits();
}
interface Cutable{
void cutPaper();
void cutRope(); //新增一个剪绳子方法
}
interface CanOpen{
void openBottle();
}
class Knives implements Chopable{
public void chopFruits() {
//切水果
}
}
class Scissors implements Cutable{
public void cutPaper() {
//剪纸
}
public void cutRope() {
}
}
class Corkscrews implements CanOpen{
public void openBottle() {
//开瓶盖
}
}
class SwissArmyKnives implements Cutable, Chopable, CanOpen{
@Override public void chopFruits() {
//重写切水果
}
@Override public void cutPaper() {
}
@Override public void cutRope() {
}
@Override public void openBottle() {
}
}
public class House {
public static void main(String[] args) {
// TODO Auto-generated method stub
Chopable knife = new Knives();
knife.chopFruits();
Cutable scissor = new Scissors();
scissor.cutPaper();
CanOpen corkscrew = new Corkscrews();
corkscrew.openBottle();
Chopable sak = new SwissArmyKnives();//多态
sak.chopFruits();
((Cutable) sak).cutPaper(); //不推荐这么做
((CanOpen) sak).openBottle();//不推荐这么做
}
}
利用接口,我们抽象出了三种特征:“能切”“能剪”“能开瓶”
水果刀和瑞士军刀同时符合能切的特征,它们都实现了切水果功能。
瑞士军刀同时实现了这三个接口,也就是说从三个不同的角度看过去,瑞士军刀是分别拥有三种功能的东西,至于剩下部分是什么样我们并不关心,我们只是想找个顺手的工具来切水果罢了。
同样的,如果我们要给能剪接口加一个方法,比如剪书,剪绳子,剪头发,又或者是改变剪纸的传入参数(极度不推荐这么做,改动旧的方法通常会造成别人或自己的程序报错,应该重载此方法,相当于新增了一个方法)我们需要在改动接口的同时一起修改剪刀类和瑞士军刀类的实现,这样保证了接口的一致性,不会出现改了半拉漏了半拉的情况。
接口通过这种方式,实现了多继承和多态。