接口值是一个两个字节长度数据结构,第一个字节包含一个指向内部表的指针(iTable),包含了存储的值的类型信息及和这个值判刑的一组方法。第二个字节指向所存储值的指针。
如果将指针赋值给接口,则类信息会存储一个指向保存的类型的指针,接口的第二个字节依旧保存指向实体值的指针。(图B)
图A
图B
2、方法接收者(方法集)
自定义类型 T 的值的指针(t *T)的方法集,由接收者为 T 和 *T 的组成,如果在指针上调用一个接受值类型的方法,go会自动将该指针解引用,并将指针所指的底层值作为方法的接收者
自定义类型 T 的值(t T)的方法集,由接收者为 T的组成。如果我们只有一个值 T,仍然可以调用一个接收者为指针类型的方法,这可以借助于go语言传值的地址的能力实现,前提是该值是可寻址的(即它是一个变量,一个解引用指针,一个数组或切片项,或者结构体中的一个可寻址字段)。因此,假设我们这样调用t.Method(),其中Method() 需要一个指针接收者,而t是一个可寻址的值,go语言会把这个调用等同于(&t).Method()。
为什么会有这种限制?
因为编译器并不是总能自动获得一个值的地址。例如:自定义类型,type Integer int , 就获取不到Integer 类型值的地址
所以不管是T 或者 *T 的方法集,在调用上是没有区别的。
接收者:
如果是T 则参数为传入参数的一个copy,在方法内的操作不会影响函数外面的值。(如果属性有引用类型,也会有影响。copy 的只是引用类型下的类型类型的指针)
如果是*T 则在函数内修改参数,影响到函数外的值(指针的用途)。
示例代码:
package main
import "fmt"
//user 结构
type user struct {
name string
email string
}
//sendMail 方法绑定在user 上
func (u user) sendMail() {
fmt.Printf("send mail to %s,%s \n", u.name, u.email)
}
//printName 方法绑定在user 指针上
func (u *user) printName() {
fmt.Printf("user name %s \n", u.name)
}
func main() {
u := user{"zhangsan", "xxx@gmail.com"}
u.sendMail()
u.printName()
u2 := &user{"zhangsan2", "xxx2@gmail.com"}
u2.sendMail()
u2.printName()
}
3、接口实现方法集:方法集定义了一组关联到给定类型的值或者指针的方法。定义方法时使用的接收者的类型决定了这个方法的关联到的值,还是关联到指针,还是两个都关联。
GO 语言规范定义的方法集规则:T 类型的方法集只包含值接收者声明的方法, *T 类型的方法集包含值接收声明的方法,也包含指针接收者声明的方法。也就是说:如果 *T 接收者来实现的一个接口,只有指向 T 类型的指针才能够实现接口。T 接收者来实现接口,那么T 和 *T 类型都能够实现了这个接口。
示例代码:
package main
import "fmt"
/**
user 类型
*/
type user struct {
name string
email string
}
/**
user 实现接口
*/
type notify interface {
notify()
}
type notify2 interface {
notify2()
}
/**
user 指针实现了 notify 接口
*/
func (u *user) notify() {
fmt.Printf("notify to user %s \n", u.name)
}
/**
user 实现了 notify 接口
*/
func (u user) notify2() {
fmt.Printf("notify2 to user %s \n", u.name)
}
/**
notify 方法,接收对数是notify 接口
*/
func notifyMethod(n notify) {
n.notify()
}
func notifyMethod2(n notify2) {
n.notify2()
}
func main() {
u := user{"zhangsan", "xxx@gmail.com"}
//不能将user 类型作为 notify 类型传递给 notifyMethod 方法,user 并没有实现 notify 接口
//notifyMethod(u)
//这样传递才可以
notifyMethod(&u)
//user 实现了 notify2 接口,那么在user 或者 *user 都实现了此接口
notifyMethod2(u)
notifyMethod2(&u)
}
注:示例中notifyMethod 函数是多态函数,只要实现了notify 接口,那么这个函数可以针对任意实体类型来执行notify 方法,这就是多态。4、类型嵌套接口实现
内部类型接口的实现会被自动提升到外部类型。如果外部类型也实现了相同的接口,那么就内部类型的实现就不会被提升。
示例代码:
package main
import "fmt"
/**
user 类型
*/
type user struct {
name string
email string
}
/**
类型嵌套
*/
type admin struct {
user
level int
}
/**
接口
*/
type notify interface {
notifyToEmail()
}
/*
*user 实现接口
*/
func (u *user) notifyToEmail() {
fmt.Printf("send mail %s to user %s \n", u.email, u.name)
}
/**
多态方法
*/
func notifyMethod(n notify) {
n.notifyToEmail()
}
func main() {
//创建一个admin
a := admin{
user: user{"name", "xxx@gmail.com"},
level: 1,
}
//由于内部类型的提升,内部类型实现的接口会自动提升到外部类型。所有 *admin 类型也实现了notify 接口。
//如果 *admin 实现notify 接口,则内部类型的实现就不会被提升
notifyMethod(&a)
}
有疑问加站长微信联系(非本文作者)