定义
方法和函数定义语法区别在于前者有实例接收参数,编译器以此确定方法所属类别。在某些语言里,尽管没有显示定义,但会在调用时隐式传递 this 实例参数。
方法集
类型有一个与之相关的方法集,这决定了它是否实现某个接口。
- 类型 T 方法集包含所有 receiver T 方法。
- 类型 *T 方法集包含所有 receiver T + *T 方法。
- 匿名嵌入 S,T 方法集包含了所有 receiver S 方法。
- 匿名嵌入 *S,T 方法集包含了所有 receiver S + *S 方法。
- 匿名嵌入 S 或 *S,*T 方法集包含了所有 receiver S + *S 方法。
可利用反射测试这些规则。
package main
import (
"fmt"
"reflect"
)
type S struct {}
type T struct {
S
}
func (S) SVal () {}
func (*S) SPtr () {}
func (T) TVal () {}
func (*T) TPtr () {}
func methodSet(s interface{}) {
t := reflect.TypeOf(s)
for i := 0; i < t.NumMethod(); i++ {
m := t.Method(i)
fmt.Println(m.Name, m.Type)
}
}
func main() {
var t T
methodSet(t)
fmt.Println("------------")
methodSet(&t)
}
面向对象的三大特征“封装”、“集成”和“多态”,Go 仅实现了部分特征,它更倾向于 “组合优于继承”这种思想。将模块分解成相互独立的更小单元,分别处理不同方面的需求,最后以匿名方式组合到一起,共同实现对外接口。而且其剪短一致的调用方式,更是隐藏了内部实现细节。
表达式
package main
import "fmt"
type N int
func (n N) test() {
fmt.Printf("test.n: %p, %d\n", &n, n)
}
func main() {
var n N = 25
fmt.Printf("main.n: %p, %d\n", &n, n)
f1 := N.test
f1(n)
f2 := (*N).test
f2(&n)
}
其输出为:
main.n: 0xc000094000, 25
test.n: 0xc000018078, 25
test.n: 0xc000094018, 25
尽管 *N 方法集包装的 test 方法 receiver 类型不同,但编译器会保证按原定义类型拷贝传值。
基于实例或指针引用的 method value,参数签名不会改变,依旧按照正常方式调用。
但当 method value 被赋值给变量或作为参数传递时,会立即计算并复制该方法执行所需要的 receiver 对象,与其绑定,以便在稍后执行时,能隐式传入 receiver 参数。
package main
import "fmt"
type N int
func (n N) test() {
fmt.Printf("test.n: %p, %v\n", &n, n)
}
func main() {
var n N = 100
p := &n
n++
f1 := n.test // 因为 test 方法的 receiver 是 N 类型,索引复制n,其等于 101
n++
f2 := p.test // 复制 *p, 等于 102
n++
fmt.Printf("main.n: %p, %v\n", p, n)
f1()
f2()
}
其输出为:
main.n: 0xc000018070, 103
test.n: 0xc00009e000, 101
test.n: 0xc000018088, 102
当然,如果目标方法的 receiver 是指针类型,那么被复制的仅是指针。
package main
import "fmt"
type N int
func (n *N) test() {
fmt.Printf("test.n: %p, %v\n", n, *n)
}
func main() {
var n N = 100
p := &n
n++
f1 := n.test // 因为 test 方法的 receiver 是 *N 类型,所以复制的是 &n
n++
f2 := p.test // 复制 p 指针
n++
fmt.Printf("main.n: %p, %v\n",p, n)
f1()
f2()
}
其输出为:
main.n: 0xc000018078, 103
test.n: 0xc000018078, 103
test.n: 0xc000018078, 103