Go 面试必考题目之 method 篇

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

内容简介:为了方便叙述,下文描述的值接收者方法简写为值方法,指针接收者方法简写为指针方法。下面代码中,哪段编号的代码会报错?具体报什么错误?

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
(t T) T and *T
(t *T) *T

使用接收者为 *T 的方法实现一个接口,那么只有那个类型的指针 *T 实现了对应的接口。   

如果使用接收者为 T 的方法实现一个接口,那么这个类型的值 T 和指针 *T 都实现了对应的接口。

声明建议

在给类声明方法时,方法接收者的类型要统一,最好不要同时声明接收者为值和指针的方法,这样容易混淆而不清楚到底实现了哪些接口。

下面我们来看看哪种类型适合声明接收者为值或指针的方法。

指针接收者方法

下面这2种情况请务必声明指针接收者方法:

1. 方法中需要对接收者进行修改的。

2. 类中包含 sync.Mutex 或类似锁的变量,因为它们不允许值拷贝。

下面这2种情况也建议声明指针接收者方法:

1. 类成员很多的,或者大数组,使用指针接收者效率更高。

2 . 如果拿不准,那也声明接收者为指针的方法吧。

值接收者方法

下面这些情况建议使用值接收者方法:

1. 类型为 mapfuncchannel

2.  一些基本的类型,如 intstring

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 面试必考题目之 method 篇


以上所述就是小编给大家介绍的《Go 面试必考题目之 method 篇》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

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

Laravel框架关键技术解析

Laravel框架关键技术解析

陈昊、陈远征、陶业荣 / 电子工业出版社 / 2016-7 / 79.00元

《Laravel框架关键技术解析》以Laravel 5.1版本为基础,从框架技术角度介绍Laravel构建的原理,从源代码层次介绍Laravel功能的应用。通过本书的学习,读者能够了解Laravel框架实现的方方面面,完成基于该框架的定制化应用程序开发。 《Laravel框架关键技术解析》第1章到第4章主要介绍了与Laravel框架学习相关的基础部分,读者可以深入了解该框架的设计思想,学习环......一起来看看 《Laravel框架关键技术解析》 这本书的介绍吧!

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

html转js在线工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具