Go 接口赋值的问题

问题描述

        在《Go语言编程》 里面讲接口赋值的时候,对对象实例赋值给接口做了如下案例 :

type Integer int

func (a Integer) Less(b Integer) bool {
    return a < b
}

func (a *Intger) Add(b Integer) {
    *a += b
}

// 定义接口
type LessAdder interface {
    Less(b Integer) bool
    Add(b Integer) 
}

func main() {
    var a Integer = 1
    var b LessAdder = &a   // (1)
}

         (1) 哪里说的是会自动生成一个新的方法

func (a *Integer) Less( b Integer) bool {
    return (*a) < b
}

        这样我们引申一下, 将Add函数也修改如下

func (a Integer) Add(b Integer) {
    a += b
}

// 赋值给接口
var a Integer = 1
var b LessAdder = &a   // (2)

        同理应该也会生成一个新的函数(如下代码):

func (a * Integer) Add(b Integer) {
    (*a) += b
}

        按新生成函数形式来看, 其调用应该会修改 a 指针的指向的值, 但如下代码结果依然是1, 请问这是为什么。

var a Integer = 1
var b LessAdder = &a
b.Add(100)
fmt.Println(a)   // 1
阅读 3k
2 个回答

不会生成一个新方法的。

Method Set:

Method sets

A type may have a method set associated with it. The method set of an interface type is its interface. The method set of any other type T consists of all methods declared with receiver type T. The method set of the corresponding pointer type *T is the set of all methods declared with receiver *T or T (that is, it also contains the method set of T). Further rules apply to structs containing embedded fields, as described in the section on struct types. Any other type has an empty method set. In a method set, each method must have a unique non-blank method name.

Receiver 是 T 的方法可以直接给 *T 用。

先说结论,Go 会判断值或指针作为方法的 caller 时,对应的方法集(method set)是否合法,但不会帮你“生成”新的方法,至少我没有从官网的文档里看到类似的说法。

楼上@fefe 已经说得很明确了,我这里只是补充一下。

首先,Go 判断接口是否被实现时会对 method set 进行判断。比如这样写的时候:

var a Integer = 1
var b LessAdder = a

如果 Integer 实现了下面的方法

func (a *Intger) Add(b Integer) {
    *a += b
}

那么就会报错

cannot use a (type Integer) as type LessAdder in assignment:
Integer does not implement LessAdder (Add method has pointer receiver)

其次,Go 在调用方法的时候,会根据 method 的 receiver 类型对 caller 做一些“转换”,比如取值或者解引用。最后的代码中 a 的直没有改变是因为调用 Add 的时候 Go 帮你对b进行了解引用,然后将(*b)的值作为一个 value 类型的 caller 传递到 Add里。这样说可能还是不太形象,结合官网对于 receiver 等价于形参的解释,实际上应该是这样的:Add(*b, 100).

所以 a 没有改变不是因为没有“生成”新方法,而是 Go 帮你做了一次值拷贝。

深究的话还是有很多不是确切知道的点,比如什么情况下会做 method set 的判断,以及什么情况下不会对 caller 取地址,等等。以上,我也是查查官网文档,如果有理解不对的地方请随时指出来。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏