表达式
根据调用者不同,方法分为两种表现形式:
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) |
参考
Q.E.D.