“针对接口编程”的真正含义是“针对超类型编程”,它利用了多态的特性。
更明确的来说就是,一个变量 a
的声明类型应该是超类型A
,所谓的超类型一般是抽象类或接口。超类型强调的是,它与它的所有派生类共有的“特性”。
这样做的好处是,变量 a
可以指向超类型 A
的任意一个派生类,并且 a
调用超类型A
中的任意一个方法都不会出错。这时 a
进行任何操作,其实都是动态决定的,而不是硬编码,这样的代码也更加有弹性。
比如,我们有下面的继承关系:
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
方法。
Dog
和 Cat
都是 Animal
的派生类,它们除了实现 makeSound
方法外,还有各自的 bark
和 meow
方法。
makeSound
方法代表了 Animal
及其所有派生类的“共性”,而 bark
和 meow
则是“非共性”。
如果我们编写了下面代码:
Dog d = new Dog();
d.bark();
那么这种代码就是“针对实现编程”,因为 d
的类型为 Dog
,是一个具体类型,而不是一个抽象类型。并且 bark
方法,也是 Dog
所特有的一种操作,而不是共性。
再来看下面代码:
Animal a = new Dog();
a.makeSound();
现在的代码就是“针对接口编程”了,因为 a
的类型是 Animal
,是一个抽象类型,而不是一个具体类型。此时 a
调用 makeSound
方法,代表的是所有的 Animal
都能进行的一种操作。
那么再进一步,我们可以将对象的创建封装成一个方法,如下:
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
方法:
String name = ... ; // name 可以动态决定
Animal a = getAnimal(name);
a.makeSound();
可以看到,name
变量可以动态决定,从而 a
也是动态决定的,调用 makeSound
时不需要关心真正的对象是什么,而且代码也不会出错。