go语言读书笔记-action系列(方法与接口)

方法简单概述

go语言中,方法是用来给用户定义的类型添加新的行为。要想给类型添加方法,只需要在函数关键字func和函数名之间加一个参数,这个参数就被称为接收者,将函数与接收者的类型绑定在一起。函数有接收者就被称为方法。

1
2
3
4
5
6
7
8
type user struct {
name string
email string
}

func (u user) notify() {
fmt.Printf("hello world!")
}

ok,其实关于这一篇读书笔记我重点想记录的是接口,关于方法大概声明就是这样,使用的套路和java、php等各种面向对象的语言都是差不多。


接口

go语言中的接口,个人感觉和java,php中这类相比,与实现者之间的联系真的是十分薄弱,其不需要使用任何显式的关键字指明要实现该接口。只要类型绑定了同样的方法就认为其实现了该接口,就可以赋值给该接口类型。也就是说go中需要实现某个接口不需要显式的指明实现哪一个,想实现哪个接口直接编写该方法就可以,不想实现后就删除该方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
type notifier inteface {
notify()
}

type user struct{
name string
email string
}

func (u *user) notify(){
fmt.Printf("Sending user email to %s<%s>\n",u.name,u.email)
}
//这就可以说user类型实现了notifier 接口
func main(){
var n notifier
n = user{"bill"}
}


接口的内部实现

image_1d5llurtkgeq6kr1oln1gj0igdm.png-126.9kB

上图展示了在user类型值赋值后接口变量的值的内部布局。接口值是一个两个字长度的数据结构,第一个字包含一个指向内部表的指针。这个内部表叫iTable,包含了所存储的值的类型信息。iTable包含了已存储的值的类型信息以及与这个值相关联的一组方法。第二个字是一个指向所存储值的指针。

image_1d5lmui17tig1l7p1ec91bv31m4k2g.png-128.5kB

当把指针赋值给接口之后,类型信息会存储一个指向保存类型的指针,而接口值第二个字依旧保存指向实体值的指针。


方法集

方法集定义了接口的接受规则。当使用指针接收者来实现接口时,值类型则无法实现该接口。这是因为值类型的方法集只包括值接收者声明的方法,所以值类型无法实现指针接收者声明的方法。、

  • 以下是go语言规范里描述的方法集:
Values Methods
T (t T)
*T (t T) and (t *T)

T类型的值的方法集只包含值接收者声明的方法。而指向T类型的指针的方法集既包含值接收者声明的方法,也包含指针接收者声明的方法。

  • 从接收者类型角度来看方法集
Methods Receivers Values
T (t T)
*T (t T) and (t *T)

这个实际上和上面那个说的是同一件事,只不过换了个视角。如果使用指针接收者来实现一个接口,那么只有指向那个类型的指针才能够实现对应的接口。如果使用值接收者来实现一个接口,那么那个类型的值和指针都能够实现对应的接口。

总结

这一篇主要记录了,go中方法的简单使用以及初步了解了下接口内部的存储方式。对于接口其实我当初在看go圣经时就一直很疑惑,为什么要有值和指针方法集这种限制,为什么不可以值也传递给指针接收者。action 中也给出了回答—编译器并不是总能自动获得一个值的地址。这就好像不可能把值传递给一个行参为指针的函数一样,编译器并不总会帮我们主动获取到值的地址。