潜在类型机制是一种代码组织和复用机制。它有一个很有意思的别名叫做鸭子类型机制,表示“如果它走起来像鸭子,叫起来也像鸭子,那么就可以将它当作鸭子对待”。因此,在代码中可以声明:“我不关心你是什么类型,只要你可以walk()和speak()就可以”。例如下面的C++代码:
#include <iostream>
using namespace std;
class Dog {
public:
void walk() {cout << "dog walk" << endl;}
void speak() {cout << "wang" << endl;}
//其他dog行为
};
class Robot {
public:
void walk() {cout << "robot walk" << endl;}
void speak() {cout << "bibi" << endl;}
//其他robot行为
};
template<class T> void perform(T something) {
something.walk();
something.speak();
}
int main() {
Dog d;
Robot r;
perform(d);
perform(r);
return 0;
}
/*输出为
dog walk
wang
robot walk
bibi*/
Java对潜在类型机制的实现
然而可惜的是Java由于擦除的原因没有支持这种特性。如果要用Java实现上例,就要用到一些其他的方法。
用接口的方式
interface Performs {
void walk();
void speak();
}
class Dog implements Performs {
public void walk() {System.out.println("dog walk");}
public void speak() {System.out.println("wang");}
//其他dog行为
}
class Robot implements Performs {
public void walk() {System.out.println("robot walk");}
public void speak() {System.out.println("bibi");}
//其他robot行为
}
public class WithInterface {
public static void perform(Performs performer) {
performer.walk();
performer.speak();
}
public static void main(String[] args) {
Dog d = new Dog();
Robot r = new Robot();
WithInterface.perform(d);
WithInterface.perform(r);
}
}//输出与上例相同
但是用接口实现潜在类型机制有一个缺陷,就是每个可以perform的类都必须实现Performs接口,而有时我们不能在声明一个类时就想到它有一天要去perform。即这种实现方式并不是真正泛化的。
用反射的方式
import java.lang.reflect.*;
class Dog {
public void walk() {System.out.println("dog walk");}
public void speak() {System.out.println("wang");}
//其他dog行为
}
class Robot {
public void walk() {System.out.println("robot walk");}
public void speak() {System.out.println("bibi");}
//其他robot行为
}
public class WithReflection {
public static void perform(Object performer) {
Class<?> pfm = performer.getClass();
try {
try {
Method walk = pfm.getMethod("walk");
walk.invoke(performer);
} catch(NoSuchMethodException e) {
System.out.println(performer + "can't walk");
}
try {
Method speak = pfm.getMethod("speak");
speak.invoke(performer);
} catch(NoSuchMethodException e) {
System.out.println(performer + "can't speak");
}
} catch(Exception e) {
throw new RuntimeException(performer.toString(), e);
}
}
public static void main(String[] args) {
Dog d = new Dog();
Robot r = new Robot();
WithReflection.perform(d);
WithReflection.perform(r);
}
}
通过反射WithReflection.perform()能动态地确定所需要的方法是否可用并调用它们。但为这么个事写反射这么长总觉得很麻烦,且它将所有的类型检查都转移到了运行时,然而很多情况下我们不希望如此。
其他
Java编程思想 中还用到了适配器仿真潜在类型机制和函数对象设计策略,参考这篇文章有所总结。