1. Duck Typing
“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。” 我们并不关心对象是什么类型,到底是不是鸭子,只关心行为。
在面向对象的编程语言中,当某个地方(比如某个函数的参数)需要符合某个条件的变量(比如要求这个变量实现了某种方法)时,如果判断这个变量是否“符合条件”的标准是: 这个变量的类型是否实现了这个要求的方法(并不要求显式地声明),那么这种语言的类型系统就可以称为 duck typing。
Python 中的duck typing
def greeting(a):
return a.sayHello()
class Duck(object):
def sayHello(self):
print('ga ga ga!')
class Person(object):
def sayHello(self):
print('Hello!')
duck = Duck()
person = Person()
greeting(duck)
greeting(person)
上面这段 Python 代码中, greeting 函数对参数 a 只有一个要求: a 必须实现 sayHello 这个方法。因为 Duck 类和 Person 类都实现了 sayHello,那么这两个类型的实例,duck 和 person,都可以用作 greeting 的参数。这就是python中duck typing的体现。
Go duck typing
使用过 Python 的人都知道写代码写起来很快。但是缺陷也是显而易见的:错误往往要在运行时才能被发现。比如上面的 greeting 函数,你可以传递任何一个变量作为参数,但是要是这个变量没有 sayHello 这个方法或者属性,那么程序运行时就会出错。相反,静态类型语言往往在编译时就是发现这类错误:如果某个变量的类型没有显式声明实现了某个方法/接口,那么,这个变量就不能用在要求一个实现了这个接口的地方。
Go 的类型系统采取了折中的办法:1)一个类型不需要显式地声明它实现了某个接口;2)但仅当某个变量的类型实现了某个接口的方法,这个变量才能用在要求这个接口的地方。
package main
import (
"fmt"
)
type Say interface {
Hello()
}
type Person struct {}
func (person *Person) Hello() {
fmt.Printf("Hello!")
}
type Duck struct {}
func (duck *Duck) Hello() {
fmt.Printf("ga ga ga!")
}
func greeting(s Say) {
s.Hello()
}
func main () {
p := Person{}
d := Duck{}
greeting(p)
greeting(d)
}
2.Go 语言接口
Go 语言提供了另外一种数据类型即接口,它把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口。
接口定义
type Namer interface {
Method1(param_list) return_type //方法名(参数列表) 返回值列表
Method2(param_list) return_type //方法名(参数列表) 返回值列表
}
接口实现
隐式实现:实现接口的类并不需要显式声明,只需要实现接口所有的函数就表示实现了该接口,而且类还可以拥有自己的方法。
package main
import "fmt"
type Shaper interface {
Area() float64
}
type Rectangle struct {
length float64
width float64
}
// 实现 Shaper 接口中的方法
func (r *Rectangle) Area() float64 {
return r.length * r.width
}
// Set 是属于 Rectangle 自己的方法
func (r *Rectangle) Set(l float64, w float64) {
r.length = l
r.width = w
}
func main() {
rt := new(Rectangle) //创建指针类型的结构体实例(类实例)
rt.Set(2, 3)
s := Shaper(rect) //这里将指针类型实例赋值给接口
s = rt //也可以
fmt.Printf("area: %f\n", s.Area())
}
接口赋值
只要两个接口拥有相同的方法列表(与次序无关),即是两个相同的接口,可以相互赋值;
接口赋值只需要接口A的方法列表是接口B的子集(即假设接口A中定义的所有方法,都在接口B中有定义),那么B接口的实例可以赋值给A的对象。
接口嵌套
接口可以嵌套,嵌套的内部接口将属于外部接口,内部接口的方法也将属于外部接口。另外在类型嵌套时,如果内部类型实现了接口,那么外部类型也会自动实现接口,因为内部属性是属于外部属性的。
type ReadWrite interface {
Read(b Buffer) bool
Write(b Buffer) bool
}
type Lock interface {
Lock()
Unlock()
}
type File interface {
ReadWrite
Lock
Close()
}
接口断言/类型转换
if v, ok : = varI.(T) ; ok { // checked type assertion<//do something
return
}
// 如果 v 是 varI 转换到类型 T 的值,ok 会是 true;否则 v 是类型 T 的零值,ok 是 false。