go thrift 无法定义方法_我的指针的方法,不是我的方法——Go的方法集

03e0bad9fb883da100a0a3b559dc31f5.png

问题

先出一道题给大家。下面的代码中,user是否实现了Namer接口?

type Namer interface {GetName() stringSetName(name string)
}type User struct {Name stringAge int
}func (u User) GetName() string {return u.Name
}func (u *User) SetName(name string) {u.Name = name
}var user User{} // user是否实现了Namer接口?

答案稍后揭晓。

方法与接收者

在Go语言中,每种命名类型都可以定义方法。方法比普通函数多一个特殊参数,叫做接收者。

接收者可以是指针类型,也可以是值类型,它们到底有什么区别?一开始学Go语言的时候,我也一头雾水。

e4b011b1c96248bfc2ad5ec43333bef8.png

指针类型的接收者之所以需要指针,就是因为它要改变原对象的值。在上面的例子中,调用user的SetName方法时,编译器会帮你把user的指针传递给SetName方法。

func main() {user.SetName("Jack") // 编译器帮你把user的指针传给SetName方法,改变了useruser.GetName() // Jack// 等价于下面两行:
(*User).SetName(&user, "Jack")User.GetName(user)
}

但如果这样调用呢:

func main() {User{}.SetName("Tom") // ERROR
}

由于User{}字面量并没有赋值给一个变量,所以没有指针,因此会报错。

函数的参数是值拷贝

现在,我们想定义一个函数,清除用户的姓名,你会怎么定义这个函数?

方案1:

func clearUserName(u User) {u.SetName("")
}

方案2:

func clearUserName(u *User) {u.SetName("")
}

想都不用想,当然选方案2了!为什么?因为go语言中,函数参数都是值拷贝,方案1传进来的是user的副本,根本没办法改变实参。

ea306fc98476bc9070dcc7c63f48d94d.png

很好,下面我把清除函数的参数换成开头定义的Namer接口

func clearNamerName(u Namer) {u.SetName("")
}

现在,我能否把user作为实参来调用clearName方法:

var user User{} // 文章开头定义的userfunc main() {clearNamerName(user) // 能否这样调用?
}

3e49039f39cdb06b181feca5f960b467.png

想一想,假如可以这样调用,那么clearNamerName方法能真的改变user的Name字段吗?

根本不可能,因为clearNamerName方法拿到的是user的副本,根本不可能改变原来的user对象。这违背了指针类型接收者的初衷:改变原对象。

方法集

如何能够在编译时就避免出现这样的问题?很简单,现在我规定,*User类型的方法包括SetName和GetName,而User类型的方法只有GetName(排除掉接收者为指针类型的SetName)。

在这个完美的规定下,判断user是否实现Namer接口时发现,user是User类型并没有SetName方法,因此user没有实现Namer接口,编译不通过!

8795d69a310a684537eaae68b98e5a15.png

当然了,这个完美的规定并不是我做的,这是方法集的概念。

对于类型T,

接收者为值类型的方法的集合为M

接收者为指针类型的方法的集合为*M

那么:

T类型的方法集为M

*T类型的方法集为M和*M

a2acb23bfd3a716b29aee2302b9b939a.gif

组合类型的方法集

下面再来一个复杂的场景

type Student struct {UserClass string
}

Student中包含了User类型的匿名字段,这是Go语言里的组合。

猜一猜Student类型的方法集是什么?是否实现了Namer接口?

d4ce683281c3be6ad9af6f600cdcd547.png

当Student类型作为函数参数传递时,会拷贝副本,其User字段也会进行拷贝,SetName就无法改变原对象的值,所以Student一定没有实现Namer接口。

当然了,*Student类型在参数传递时只会拷贝指针,不会拷贝值,因此*Student类型是实现了Namer接口的。

如何避免这个问题?聪明的你一定想到了。

type Student struct {*UserClass string
}

没错,只要内嵌字段改为指针类型即可。这样拷贝Student的时候,User字段只会拷贝指针,不会拷贝值。

对于组合结构,方法集这样定义

若类型S包含匿名字段T,则S的方法集也包含T的方法集。

若类型S包含匿名字段*T,则S的方法集也包含T和*T的方法集。

无论类型S包含T还是*T,*S都包含T和*T的方法集。

在判断一个类型是否实现一个接口时,编译器会严格按照接口集来校验。而对于一个该类型的实例,完全可以调用其指针的方法集,编译器会自动帮你转换。

一、Go语言的由来      Go语言亦叫Golong语言,是由谷歌Goggle公司推出。Go语言的主要开发者有:肯.汤姆逊(Ken Thompson)、罗布.派克(Rob Pike)和罗伯特.格里泽默(Robert Griesemer)。这三个都是大神,稍介绍一下他们的贡献:     肯.汤姆逊(Ken Thompson):图灵奖得主,Uinx发明人,B语言作者(C语言前身),还做飞行员,后来被谷歌挖走。     罗布.派克(Rob Pike):Unix团队和Plan 9操作系统计划的成员,与Ken老爷子共事多年,并共创出广泛使用的UTF-8 字元编码。     罗伯特.格里泽默(Robert Griesemer):曾协助制作Java的HotSpot编译器,和Chrome浏览器的JavaScript引擎V8。     膜拜一下大神的容颜:Ken老爷子(左),Rob Pike(右)         二、开发Go语言的初衷     根据Go语言开发者自述,近10多年,从单机时代的C语言到现在互联网时代的Java,都没有令人满意的开发语言,而 C++往往给人的感觉是,花了100%的经历,却只有60%的开发效率,产出比太低,Java和C#的哲学又来源于C++。并且,随着硬件的不断升级,这些语言不能充分的利用硬件及CPU。因此,一门高效、简洁、开源的语言诞生了。 三、Go语言的特点    Go语言保证了既能到达静态编译语言的安全和性能,又达到了动态语言开发速度和易维护性,有人形容Go语言:Go = C + Python , 说明Go语言既有C静态语言程序的运行速度,又能达到Python动态语言的快速开发。 Go语言有以下特性: 1.自动垃圾回收     C/C++最头疼的就是指针问题,一不小心就野指针了或者又越界了。在Go语言里再也不用担心,也不用考虑delete或者free,系统自动会回收。 2.函数可以返回多个值     这个很神奇,大多数语言只能返回一个值,Go语言可以返回多个值。这个功能使得开发者再不用绞尽脑汁的想到底怎么返回值的设计,也不用为了传值专门定义一个结构体。 3.并发编程     Go语言天然并发,只需要关键字“go”就可以让函数并发执行,使得并发编程变得更为简单,这也是Go语言最大的优势。 四、Go语言能做什么开发     Go语言是非常有潜力的语言,是因为它的应用场景是目前互联网非常热门的几个领域,比如区块链开发、大型游戏服务端开发、分布式/云计算开发。像Goggle、阿里、京东等互联网公司都开始用Go语言开发自己的产品。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值