鸭子类型(duck typing)是动态类型的一种风格。在这种风格中一个对象的有效语义不是由继承自特定的类或实现特定的接口,而是由当前的方法和属性的集合决定。
"当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。"
在鸭子类型中,关注的不是对象的类型本身,而是它是如何使用的。
由于鸭子类型机制常见于动态语言,我们先看个Python的例子:
class Duck:
def quack(self):
print "鸭子叫"
def feathers(self):
print "鸭子有羽毛"
class Person:
def quack(self):
print "模仿鸭子叫"
def feathers(self):
print "人没有羽毛"
def in_the_forest(duck):
duck.quack()
duck.feathers()
def game():
donald = Duck()
john = Person()
in_the_forest(donald)
in_the_forest(john)
game()
在该例中in_the_forest方法不关注传入的参数是那种类型,而关心该对象是否有特定的方法。
Java 实现鸭子类型
- 其中一种方式是通过多态
public interface Performer {
void eat();
void walk();
}
public class Dog implements Performer {
@Override
public void eat() {
System.out.println("狗吃狗粮");
}
@Override
public void walk() {
System.out.println("狗跑的很快");
}
}
public class Cat implements Performer {
@Override
public void eat() {
System.out.println("猫吃猫粮");
}
@Override
public void walk() {
System.out.println("猫走路很轻盈");
}
}
public class T {
public static void main(String[] args) {
Dog dog = new Dog();
Cat cat = new Cat();
perform(dog);
perform(cat);
}
static <T extends Performer> void perform(T duck){
duck.eat();
duck.walk();
}
}
严格来说这种实现并非准确的鸭子模型,因为perform方法的参数必须实现了Performer接口,即关注了对象的本身的类型
- 常用方式是使用反射
public class Cat {
public void eat() {
System.out.println("猫吃猫粮");
}
public void walk() {
System.out.println("猫走路很轻盈");
}
}
public class Dog {
public void eat() {
System.out.println("狗吃狗粮");
}
public void walk() {
System.out.println("狗跑的很快");
}
}
public class T {
public static void main(String[] args) {
Dog dog = new Dog();
Cat cat = new Cat();
try {
perform(dog);
perform(cat);
} catch (Exception e){
e.printStackTrace();
}
}
static void perform(Object duck) throws Exception {
Class clazz = duck.getClass();
Method eat = clazz.getMethod("eat");
eat.invoke(duck);
Method walk = clazz.getMethod("walk");
walk.invoke(duck);
}
}
比起使用多态,使用反射更接近于鸭子模型,perform方法不关心传入的对象的类型,只关心该对象是否拥有指定的方法。