欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

聊一聊“鸭子类型“

程序员文章站 2022-05-04 08:42:12
“鸭子类型"也叫"鸭式辨型"等, 英文叫"Duck Typing”.下面这句话不知道是谁说的, 只记得之前在读《Javascript权威指南》时遇到过当看到一只动物走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只动物就可以被称为鸭子。按我的话来说, 一个东西, 我不想管它是什么, 我只管它能做什么.在程序语言中, 这种所谓的"鸭子类型"表现有两种形式: 动态和静态.动态的例子:def work(obj):obj.work()class Programmer:def wor...

“鸭子类型"也叫"鸭式辨型"等, 英文叫"Duck Typing”.

下面这句话不知道是谁说的, 只记得之前在读《Javascript权威指南》时遇到过

当看到一只动物走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只动物就可以被称为鸭子。

按我的话来说, 一个东西, 我不想管它是什么, 我只管它能做什么.

在程序语言中, 这种所谓的"鸭子类型"表现有两种形式: 动态和静态.

动态的例子:

def work(obj):
	obj.work()
	
class Programmer:
	def work(self):
		print("coding")
	
class Student:
	def work(self):
		print("learning")
	
if __name__ == '__main__':
	s = Student()
	p = Programmer()
	
	work(s)
	work(p)

代码开始定义的work(obj)函数是说: 你给我一个东西, 它只要能work()就行, 至于它是什么, 无所谓.
十分灵活.

但是这有个问题, 如果你传入一个没有work()方法的obj, 运行期间会报错, 比如:

# AttributeError: 'dict' object has no attribute 'work'
work({})

那么很明显, 如果我写了这个work函数, 别人随便传的话, 很容易出错, 这也是动态类型的缺点, 很难有效的限制参数的类型, 运行期容易出现类型方面的错误.
正所谓

动态类型一时爽,代码重构火葬场.

那么静态的怎么样呢?
以传统的C++, Java来说, 你必须实现相应的接口才可以, 不然编译都过不了, 这也就避免了一些因类型不对导致的问题:

// Obj.java
package com.test.service;

// 定义接口
public interface Obj {
	public void work();
}

// Student.java
package com.test.impl;
import com.test.service.Obj;
// Student实现了Obj接口, 所以可以work()
public class Student implements Obj {
	public void work() {
		System.out.println("learning");
	}
}

// FuErDie.java
package com.test.impl;
// FuErDie没有实现Obj接口, 所以不能work
public class FuErDie {
	public void play() {
		System.out.println("play...");
	}
}

// Application.java
package com.test.app;

import com.test.impl.FuErDie;
import com.test.impl.Student;
import com.test.service.Obj;

public class Application {
	public static void work(Obj obj) {
		obj.work();
	}
	public static void main(String[] args) {
		Student s = new Student();
		work(s); // 输出 learning
		
		FuErDie f = new FuErDie();
		// 报错: The method work(Obj) in the type Application is not applicable for the arguments (FuErDie)
		work(f);
	}
}

上面的例子很好地体现了静态语言的一些优势: 编译期间就可以检测到一些类型方面的错误.

而go语言的Duck Typing有点特别, 它结合了动态语言的灵活性,同时又会进行静态语言的类型检查,写起来是比较方便的。go 采用了折中的做法:不要求类型显示地声明实现了某个接口,只要实现了相关的方法即可,编译器就能检测到。

package main

import (
	"fmt"
)

type Work interface {
	work()
}

type Student struct {}

func (s *Student) work() {
	fmt.Println("learning")
}

func (s Student) String() string {
	return "不告诉你"
}

type FuErDie struct {}
func (f *FuErDie) play() {
	fmt.Println("play...")
}

func work(obj Work) {
	obj.work()
}

func main() {
	s := &Student{}
	work(s)

	f := &FuErDie{}
	// ./main.go:35:6: cannot use f (type *FuErDie) as type Work in argument to work:
	//        *FuErDie does not implement Work (missing work method)
	work(f)

	fmt.Println(s)
}

可以看到, 在go中实现一个接口, 不需要显示地写"implement xxx", 只要实现其中的方法就行.
而且接口与其具体的实现之间几乎没有耦合.
所以在go中, 你可能无意间实现了很多接口.

欢迎补充指正!

(完)

本文地址:https://blog.csdn.net/butterfly5211314/article/details/107591816

相关标签: Golang