针对接口编程”的真正含义是“针对超类型编程”,它利用了多态的特性。

更明确的来说就是,一个变量 a声明类型应该是超类型A,所谓的超类型一般是抽象类接口。超类型强调的是,它与它的所有派生类共有的“特性”。

这样做的好处是,变量 a 可以指向超类型 A 的任意一个派生类,并且 a 调用超类型A 中的任意一个方法都不会出错。这时 a 进行任何操作,其实都是动态决定的,而不是硬编码,这样的代码也更加有弹性。

比如,我们有下面的继承关系:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
interface Animal {
void makeSound();
}
class Dog implements Animal {
public void makeSound() {
bark();
}
public void bark() {
// 汪汪叫
}
}
class Cat implements Animal {
public void makeSound() {
meow();
}
public void meow() {
// 喵喵叫
}
}

Animal 是一个接口(也可以是抽象类),其中包含了 makeSound 方法。

DogCat 都是 Animal 的派生类,它们除了实现 makeSound 方法外,还有各自的 barkmeow 方法。

makeSound 方法代表了 Animal 及其所有派生类的“共性”,而 barkmeow 则是“非共性”。

针对实现编程

如果我们编写了下面代码:

1
2
Dog d = new Dog();
d.bark();

那么这种代码就是“针对实现编程”,因为 d 的类型为 Dog,是一个具体类型,而不是一个抽象类型。并且 bark 方法,也是 Dog 所特有的一种操作,而不是共性。

针对接口编程

再来看下面代码:

1
2
Animal a = new Dog();
a.makeSound();

现在的代码就是“针对接口编程”了,因为 a 的类型是 Animal,是一个抽象类型,而不是一个具体类型。此时 a 调用 makeSound 方法,代表的是所有的 Animal 都能进行的一种操作。

那么再进一步,我们可以将对象的创建封装成一个方法,如下:

1
2
3
4
5
6
7
8
9
10
11
public static Animal getAnimal(String name) {
Animal a = null;
if (name.equals("dog")) {
a = new Dog();
} else if (name.equals("cat")) {
a = new Cat();
}
return a;
}

这样使用 getAnimal 方法:

1
2
3
String name = ... ; // name 可以动态决定
Animal a = getAnimal(name);
a.makeSound();

可以看到,name 变量可以动态决定,从而 a 也是动态决定的,调用 makeSound 时不需要关心真正的对象是什么,而且代码也不会出错。