这是我参与「第四届青训营 」笔记创作活动的的第10天
定义
鸭子类型来源于著名的“鸭子测试”:
当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。
同理,如果某个对象O具备类型T的属性和方法,那么就可以认为对象O就是类型T,这就是鸭子类型。
鸭子类型一般是在弱类型语言(如JavaScript、Python等)或者一些非侵入式接口的语言(如Go)才存在。
如果一个东西有两个孔,而且能插插头,那它就是插座(逃
示例
下面以JavaScript为例示范一下鸭子类型:
var socket = {
hasTwoHoles: true,
pluggable: true,
color: "black"
};
var someAnimal = {
appearance: "feathers",
quack: function animal_quack(what) {
print(what + " whoof-whoof!");
},
eyes: "yellow"
};
function check(who) {
if ((who.appearance == "feathers") && (typeof who.quack == "function")) {
who.quack("I look like a duck!\n");
return true;
}
return false;
}
check(duck); // true
check(someAnimal); // true
其中,我们定义了两个对象,一个是socket,另一个是pigNose。从代码中可以看到,它们虽然是截然不同的两个对象,但是它们都有两个孔,都可以插插头,插了之后都有反应,因此可以认为它们都是一种“插座”。
运行结果如下:
与其他类型系统的比较
结构类型系统
鸭子类型和结构类型相似但与之不同。结构类型由类型的结构决定类型的兼容性和等价性,而鸭子类型只由结构中在运行时所访问的部分决定类型的兼容性
接口
接口可以提供鸭子类型的一些益处,但鸭子类型与之不同的是没有显式定义任何接口。例如,如果一个第三方Java库实现了一个用户不允许修改的类,用户就无法把这个类的实例用作一个自己定义的接口的实现,而鸭子类型允许这样做。
但是如果如果某种语言支持非侵入式的接口实现,那么该语言也支持鸭子类型(如Go)
模板或泛型
模板函数或方法在一个静态类型上下文中应用鸭子测试;这同时带来了静态和动态类型检查的一般优点和缺点。同时,由于在鸭子类型中,只有“在运行时被实际调用的”方法需要被实现,而模板要求实现“在编译时不能证明不可到达的”所有方法,因此鸭子类型更具有可伸缩性。
实例包括带有模板的C++语言和Java语言的泛型。