分享
  1. 首页
  2. 文章

Go面试必考题目之method篇

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

  在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/>


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

本文来自:简书

感谢作者:deletelazy

查看原文:Go面试必考题目之method篇

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

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

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏