golang中接口赋值与方法集

栏目: Go · 发布时间: 7年前

内容简介:报的错是既然如此,修改为

接口使用疑问

golang 中的接口可以轻松实现 C++ 中的多态,而且没有 继承自同一父类 的限制,感觉方便很多。但是在使用的时候,如果没有理解,也可能会遇到"坑"。比如 《Go语言实战》 中的一个例子:

package main

import "fmt"

type user struct {
    name  string
    email string
}
type notifier interface {
    notify()
}

func (u *user) notify() {
    fmt.Printf("sending user email to %s<%s>\n",
        u.name,
        u.email)
}
func sendNotification(n notifier) {
    n.notify()
}

func main() {
    u := user{
        name:  "stormzhu",
        email: "abc@qq.com",
    }
    sendNotification(u) 
}
// compile error
// cannot use u (type user) as type notifier in argument to sendNotification:
//    user does not implement notifier (notify method has pointer receiver)

报的错是 u 没有实现 notifier 这个接口,实现了这个接口的是 *user 类型,而不是 user 类型, uuser 类型,所以不能赋值给 notifier 这个接口。

既然如此,修改为 sendNotification(&u) 就OK了。然而问题是,如何理解到底是 T 类型还是 *T 类型实现了某个接口呢?

接口的定义

参考雨痕的 《Go语言学习笔记》 第七章, go 语言中的接口定义如下:

type iface struct {
    tab  *itab          // 类型信息
    data unsafe.Pointer //实际对象指针
}
type itab struct {
    inter *interfacetype // 接口类型
    _type *_type         // 实际对象类型
    fun   [1]uintptr     // 实际对象方法地址
}

虽然具体的细节操作不太懂,但是可以知道,对一个接口赋值的时候,会拷贝 类型信息 和该类型的 方法集 。这就类似于 C++ 多态中的 虚指针 ( vptr )和 虚函数表 ( vtable )了。我理解的是,只要这个类型的方法集中包括这个接口的所有方法,那么它就是实现了这个接口,才能够赋值给这个接口,那么问题来了,一个类型的方法集是什么呢?

方法集

同样参考雨痕 《Go语言学习笔记》 第6章6.3节,书中总结的很全面:

  • 类型 T 的方法集包含所有 receiver T 方法。
  • 类型 *T 的方法集包含所有 receiver T + *T 方法。
  • 匿名嵌入 S ,类型 T 的方法集包含所有 receiver T + S 方法。
  • 匿名嵌入 *S ,类型 T 的方法集包含所有 receiver T + S + *S 方法。
  • 匿名嵌入 S*S ,类型 *T 的方法集包含所有 receiver T + *T + S + *S 方法。

虽然看起来比较复杂,但总结完就一话, *T 类型就是厉害,方法集包括 T*T 的方法。

所以文章开头的例子中, uuser 类型,方法集是空的,不算是实现了 notifier 接口。

当在纠结应该将 T 类型还是 *T 类型赋值给某个接口的时候,第一步就是看方法集,看一看该类型到底有没有实现这个接口。(所以 T*T 不是一个类型。。。)

一些例子

go 语言的内置库中有定义了很多接口,如 error 接口,

type error interface {
    Error() string
}

内置的 errors 包实现了这个接口:

// Package errors implements functions to manipulate errors.
package errors

// New returns an error that formats as the given text.
func New(text string) error {
    return &errorString{text}
}

// errorString is a trivial implementation of error.
type errorString struct {
    s string
}

func (e *errorString) Error() string {
    return e.s
}

可以看到 New 方法返回值是 error 接口,而只有 *errorString 类型实现了这个接口,所以 New 方法返回的是 &errorString{text} 而不是 errorString{text}

总结

  • T*T 不是一个类型,他们的方法集不同
  • 类型 *T 的方法集包含所有 receiver T + *T 方法,类型 T 的方法集只包含所有 receiver T 方法。

我的简书博客

参考


以上所述就是小编给大家介绍的《golang中接口赋值与方法集》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

云攻略

云攻略

马克·贝尼奥夫、卡莱尔·阿德勒 / 徐杰 / 海天出版社 / 2010年8月 / 36.00元

Apple、Google、甲骨文、腾讯 都已投入了云的怀抱, 你还在等什么? 快来加入我们! 最初,Salesforce.com 只是一间小小的租赁公寓 在短短10年内 它已成长为 世界上发展最快、最具创新力的 产业变革领导者 曾经,这是个软件为王的时代。 现在,这是个云计算的新时代。 NO SOFTWARE 抛弃软件的......一起来看看 《云攻略》 这本书的介绍吧!

SHA 加密
SHA 加密

SHA 加密工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

html转js在线工具
html转js在线工具

html转js在线工具