Go面试必考题目之method篇

  在Go的类方法中,分为值接收者方法和指针接收者方法,对于刚开始接触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)

}

```

  抛砖引玉,让我们学习完再来作答。

### 值方法和指针方法

我们来看看值方法的声明。

```Go

type Dog struct {

}

func (d Dog) Bark() {

    fmt.Println("dog")

}

```

上面代码中,方法`Bark`的接收者是值类型,那么这就是一个值接收者的方法。

下面再看看指针接收者的方法。

```Go

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是要能取地址才能这么调用,比如下面这种情况就不行:

```Go

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种情况请务必声明指针接收者方法:

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

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

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

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

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

#### 值接收者方法

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

- 类型为`map`,`func`,`channel`。

- 一些基本的类型,如`int`,`string`。

- 一些小数组,或小结构体并且不需要修改接收者的。

### 题目解析

```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) 通过,上面说了类型*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的方法

}

```

### 总结

- 理清类型的方法集合。

- 理清接收者方法的调用范围。

### 参考文献

- 《Pointer vs Value receiver》

https://yourbasic.org/golang/pointer-vs-value-receiver/

- 《Method sets》

https://golang.org/ref/spec#Method_sets

- https://stackoverflow.com/questions/19433050/go-methods-sets-calling-method-for-pointer-type-t-with-receiver-t?rq=1

#### 感谢阅读,欢迎大家指正,留言交流~

<img src="https://user-gold-cdn.xitu.io/2019/5/22/16adfd582258b45b?w=1005&h=1164&f=jpeg&s=217262" width=300 height=330 align=center/>

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容