表达式

根据调用者不同,方法分为两种表现形式:

instance.method(args...) ---> <type>.func(instance, args...)

前者称为 method value,后者 method expression

两者都可像普通函数那样赋值和传参,区别在于 method value 绑定实例,而 method expression 则须显式传参。

package main

import "fmt"

type User struct {
	id   int
	name string
}

func (self *User) Test() {
	fmt.Printf("%p, %v\n", self, self)
}

func main() {
	//instance
	u := User{1, "Tom"}
	u.Test()

	//method value
	mValue := u.Test // instance.method(args...)
	mValue() // 隐式传递 receiver

	//method expression
	mExpression := (*User).Test //< type>.func(instance, args...) 此处type与方法中receiver类型相同
	mExpression(&u) // 显式传递 receiver
}

需要注意,method value 会复制 receiver

package main

import "fmt"

type User struct {
	id   int
	name string
}

func (self User) Test() {
	fmt.Println(self)
}

func main() {
	u := User{1, "Tom"}
	mValue := u.Test // 立即复制 receiver,因为不是指针类型,不受后续修改影响。
	//如果方法receiver是指针类型,后续修改就会被影响
	u.id, u.name = 2, "Jack"
	u.Test() //{2 Jack}

	mValue() //{1 Tom}
}

方法的签名变更

方法的签名会随着调用表达式的变化而变化(不知道这种表述是否准确

还是上面的例子

package main

import "fmt"

type User struct {
	id   int
	name string
}

func (self *User) TestPointer() {
	fmt.Printf("TestPointer: %p, %v\n", self, self)
}

func (self User) TestValue() {
	fmt.Printf("TestValue: %p, %v\n", &self, self)
}

func main() {
	u := User{1, "Tom"}
	fmt.Printf("User: %p, %v\n", &u, u)

	mv := User.TestValue
	mv(u)

	mp := (*User).TestPointer
	mp(&u)

	mp2 := (*User).TestValue // *User 方法集包含 TestValue。
	mp2(&u)                  // 签名变为 func TestValue(self *User)
    /*
    	可以看到,对于方法 TestValue 的声明 应该是 (self User) TestValue()
    	也就是应该由实例来调用,而不是地址
    	但是此处将函数签名更改为了 func TestValue(self *User)
    	也就是实际的执行还是看你的调用方式
    */
    // 实际依然是 receiver value copy。
} 

综上,我们总结一下表达式和方法的各种情形

函数声明(方法)调用方式(表达式)实际运行
(self A) demo()a.demo()A.demo(a)
A.demo(a)(A).demo(a)
(*A).demo(*a)(*A).demo(*a)
(self *A) demo()a.demo()(*A).demo(*a)
A.demo()A 无法调用 *A 方法
(*A).demo(*a)(*A).demo(*a)

参考

Go 边看边练

Q.E.D.


此 生 无 悔 恋 真 白 ,来 世 愿 入 樱 花 庄 。