Go面试必考题目之method篇

deletelazy · · 832 次点击 · 开始浏览    置顶
这是一个创建于 的主题,其中的信息可能已经有所发展或是发生改变。

#### 来源:微信公众号《Go后端干货》 #### 各种Go,后端技术,面试题分享,欢迎关注 #### 公众号上排版更好 <img src="https://static.studygolang.com/190508/d20b3d9f2790729effb6cbea985e443d.png" width=300 height=330 /> 在Go的类方法中,分为值接收者方法和指针接收者方法, 对于刚开始接触Go的同学来说,有时对Go的方法会感到困惑。下面我们结合题目来学习Go的方法。 为了方便叙述,下文描述的值接收者方法简写为值方法,指针接收者方法简写为指针方法。 下面代码中,哪段编号的代码会报错?具体报什么错误? ``` type Animal interface { Bark() } type Dog struct { } func (d Dog) Bark() { fmt.Println("dog") } type Cat struct { } func (c *Cat) Bark() { fmt.Println("cat") } func Bark(a Animal) { a.Bark() } func getDog() Dog { return Dog{} } func getCat() Cat { return Cat{} } func main() { dp := &Dog{} d := Dog{} dp.Bark() // (1) d.Bark() // (2) Bark(dp) // (3) Bark(d) // (4) cp := &Cat{} c := Cat{} cp.Bark() // (5) c.Bark() // (6) Bark(cp) // (7) Bark(c) // (8) getDog().Bark() // (9) getCat().Bark() // (10) } ``` 抛砖引玉,让我们学习完再来作答。 ### 值方法和指针方法 我们来看看值方法的声明。 ``` type Dog struct { } func (d Dog) Bark() { fmt.Println("dog") } ``` 上面代码中,方法Bark的接收者是值类型,那么这就是一个值接收者的方法。 下面再看看指针接收者的方法。 ``` type Cat struct { } func (c *Cat) Bark() { fmt.Println("cat") } ``` #### 类的方法集合 这个在Go文档里有定义: 对于类型T,它的方法集合是所有接收者为T的方法。 对于类型*T,它的方法集合是所有接收者为*T和T的方法。 Values | Method Sets ----|---- T | (t T) *T | (t T) and (t *T) #### 方法的调用者 指针*T接收者方法:只有指针类型*T才能调用,但其实值T类型也能调用,为什么呢?因为当使用值调用t.Call()时,Go会转换成(&t).Call(),也就是说最后调用的还是接收者为指针*T的方法。 但要注意t是要能取地址才能这么调用,比如下面这种情况就不行: ``` func getUser() User { return User{} } ... getUser().SayWat() // 编译错误: // cannot call pointer method on aUser() // cannot take the address of aUser() 值T接收者方法:指针类型*T和值T类型都能调用。 ``` Methods | Receivers Values (t T) | T and *T (t *T) | *T 使用接收者为*T的方法实现一个接口,那么只有那个类型的指针*T实现了对应的接口。 如果使用接收者为T的方法实现一个接口,那么这个类型的值T和指针*T都实现了对应的接口。 ### 声明建议 在给类声明方法时,方法接收者的类型要统一,最好不要同时声明接收者为值和指针的方法,这样容易混淆而不清楚到底实现了哪些接口。 下面我们来看看哪种类型适合声明接收者为值或指针的方法。 #### 指针接收者方法 下面这2种情况请务必声明指针接收者方法: 1. 方法中需要对接收者进行修改的。 2. 类中包含sync.Mutex或类似锁的变量,因为它们不允许值拷贝。 下面这2种情况也建议声明指针接收者方法: 1. 类成员很多的,或者大数组,使用指针接收者效率更高。 2. 如果拿不准,那也声明接收者为指针的方法吧。 #### 值接收者方法 下面这些情况建议使用值接收者方法: 1. 类型为map,func,channel。 2. 一些基本的类型,如int,string。 3. 一些小数组,或小结构体并且不需要修改接收者的。 #### 题目解析 ``` type Animal interface { Bark() } type Dog struct { } func (d Dog) Bark() { fmt.Println("dog") } type Cat struct { } func (c *Cat) Bark() { fmt.Println("cat") } func Bark(a Animal) { a.Bark() } func getDog() Dog { return Dog{} } func getCat() Cat { return Cat{} } func main() { dp := &Dog{} d := Dog{} dp.Bark() // (1) 通过 d.Bark() // (2) 通过 Bark(dp) // (3) 通过,上面说了类型*Dog的方法集合包含接收者为*Dog和Dog的方法 Bark(d) // (4) 通过 cp := &Cat{} c := Cat{} cp.Bark() // (5) 通过 c.Bark() // (6) 通过 Bark(cp) // (7) 通过 Bark(c) // (8) 编译错误,值类型Cat的方法集合只包含接收者为Cat的方法 // 所以T并没有实现Animal接口 getDog().Bark() // (9) 通过 getCat().Bark() // (10) 编译错误, // 上面说了,getCat()是不可地址的 // 所以不能调用接收者为*Cat的方法 } ``` #### 总结 1. 理清类型的方法集合。 2. 理清接收者方法的调用范围。 #### 参考文献 1.《Pointer vs Value receiver》 https://yourbasic.org/golang/pointer-vs-value-receiver/ 2.《Method sets》 https://golang.org/ref/spec#Method_sets 3. https://stackoverflow.com/questions/19433050/go-methods-sets-calling-method-for-pointer-type-t-with-receiver-t?rq=1 感谢阅读,欢迎大家指正,留言交流~

有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

832 次点击  
加入收藏 微博
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传