📌Golang📌基础📌O-方法.txt
Golang方法总是绑定对象实例,并隐式将实例作为第一实参(receiver)。
• 只能为当前包内命名类型定义方法。
• 参数receiver可任意命名。如方法中未曾使用,可省略参数名。
• 参数receiver类型可以是T或*T。基类型T不能是接口或指针。
• 不支持方法重载,receiver只是参数签名的组成部分。
• 可用实例value或pointer调用全部方法,编译器自动转换。
一个方法就是一个包含了接受者的函数,接受者可以是命名类型或者结构体类型的一个值或者是一个指针。
所有给定类型的方法属于该类型的方法集。
方法定义:
func (recevier type) methodName(参数列表)(返回值列表){}
参数和返回值可以省略
type MyInt int
func (i MyInt) String() string {
return strconv.Itoa(int(i))
}
func main() {
i := MyInt(4)
println(i.String())
}
当接受者不是一个指针时,该方法操作对应接受者的值的副本,而不是指针操作。
当接受者是指针时,即使用值类型调用那么函数内部也是对指针的操作。
type C struct{}
func (*C) Pointer() {
println("pointer")
}
func (C) Value() {
println("value")
}
func main() {
c := C{}
c.Pointer()
c.Value()
p := &c
p.Pointer()
p.Value()
}
struct匿名字段,可以像字段成员那样访问匿名字段方法,编译器负责查找。
通过匿名字段,可获得和继承类似的复用能力。依据编译器查找次序,只需在外层定义同名方法,就可以实现"override"。
type Base struct{}
type Extend struct {
*Base
}
func (*Base) Do() {
println("d")
}
func (*Base) Say() {
println("b")
}
func (*Extend) Say() {
println("e")
}
func main() {
e := &Extend{&Base{}}
e.Do() //do
e.Say() //e
e.Base.Say() //b
}
每个类型都有与之关联的方法集,这会影响到接口实现规则。
• 类型T方法集包含全部receiver为T的方法。
• 类型*T方法集包含全部receiver为T和*T方法。
• 如类型S包含匿名字段T,则S和*S方法集包含T方法。
• 如类型S包含匿名字段*T,则S和*S方法集包含T和*T方法。
• 不管嵌入T或*T,*S方法集总是包含T和*T方法。
用实例value和pointer调用方法(含匿名字段)不受方法集约束,编译器总是查找全部方法,并自动转换receiver实参。
Go语言中内部类型方法集提升的规则:
类型T方法集包含全部receiver为T的方法。
类型*T方法集包含全部receiver为T和*T的方法。
如类型S包含匿名字段T,则S和*S方法集包含T方法。
当嵌入一个类型,嵌入类型的接受者为值类型的方法将被提升,可以被外部类型的值和指针调用。
如类型S包含匿名字段*T,则S和*S方法集包含T和*T方法。
当我们嵌入一个类型的指针,嵌入类型的接受者为值类型或指针类型的方法将被提升,可以被外部类型的值或者指针调用。
根据调用者不同,方法分为两种表现形式:
instance.method(args...) ---> <type>.func(instance, args...)
前者称为method-value,后者method-expression。
两者都可像普通函数那样赋值和传参,区别在于method-value绑定实例,而method-expression则须显式传参。
type User struct {
Name string
Age uint8
}
func (u *User) Desc() {
println("My name is", u.Name, ", I'm", u.Age, "years old.")
}
func main() {
u := &User{
Name: "Tom",
Age: 9,
}
u.Desc()
v := u.Desc
v() //隐匿传递receiver
e := (*User).Desc
e(u) //显式传递receiver
}