golang语言 方法
接收者类型必须为形式 T 或 *T, T 称为接收者的 基础类型, 它不能为指针或接口类型
声明 func (p *Point) Scale(factor float64) {...} 将接收者类型为 *Point 的方法 Scale 绑定至基础类型 Point。
必须在同一包中声明方法。
给类型别名声明的方法,效果等同于给该类型声明的方法
nil值的T或*T类型变量拥有对应的方法,可以安全的调用
约定:如果某类型有一个以指针作为接收者的方法,那么该类型的所有方法都以指针作为接收者
值方法可通过指针和值调用, 而指针方法只能通过指针来调用。
之所以会有这条规则是因为指针方法可以修改接收者;通过值调用它们会导致方法接收到该值的副本, 因此任何修改都将被丢弃,因此该语言不允许这种错误。不过有个方便的例外:若该值是可寻址的, 那么就会自动插入取址操作符来调用指针方法。这仅仅是个语法糖,T类型的值不拥有所有*T指针的方法
综上,接受者为指针的方法,可以用值去调用,接受者为值的方法,可以用指针去调用
指针接收者方法可以改变接收者的值(即使是用值去调用,也能改变接收者的值),
值接收者方法改变的是接收者的副本(即使是用指针去调用,也不能改变接收者的值),
类型T的方法集由接收者为T的方法构成
类型*T的方法集由接收者为T和*T的方法构成
给定结构类型 S 与名为 T 的类型,包含在结构方法集中的已提升方法如下:
- 若 S 包含一个匿名字段 T,则 S 的方法集包含带接收者 T 的已提升方法。*S 的方法集包含带接收者 T 和 *T 的已提升方法。
- 若 S 包含匿名字段 *T,则 S 与 *S 的方法集均包含带接收者 T 或 *T 的已提升方法。
内嵌字段会指导编译器去生成额外的包装方法来委托已经声明好的方法
ColoredPoint包含内嵌字段Point,Point有Distance方法,编译器会为ColoredPoint添加以下方法:
func (p ColoredPoint) Distance(q Point) float64 { //在这个方法里,不能访问ColoredPoint的字段
return p.Point.Distance(q)
}
所以求两个ColoredPoint变量p和q的Distance时,不能用p.Distance(q),而要用p.Distance(q.Point)
如果S内嵌了T,编译器会隐式的为S声明T的方法,并为*S声明T和*T的方法
编译器会自动隐式声明一个和方法相对应的函数,接受者作为第一个参数。
方法只是个带接收者参数的函数,方法的类型就是将接收者作为第一个实参的函数类型。
例如,方法 Scale 拥有类型 func(p *Point, factor float64)
然而,通过这种方式声明的函数不是方法。
type Book struct { pages int } var book Book func (b Book) Pages() int { return b.pages } // 方法 func (b *Book) SetPages(pages int) { b.pages = pages } func Book.Pages(b Book) int { return b.pages } // 隐式声明对应的函数 func (*Book).SetPages(b *Book, pages int) { b.pages = pages } (*Book).SetPages(&book, 123) // 可以调用隐式声明的函数 Book.Pages(book)
当为T声明一个方法时,编译器会隐式为*T声明相同的方法,
所以当为T声明方法时,编译器会隐式声明一个隐式方法和两个隐式函数
方法值
type T struct { a int } func (tv T) Mv(a int) int { return 0 } // 值接收者 func (tp *T) Mp(f float32) float32 { return 1 } // 指针接收者 var t T
T.Mv 将产生一个等价于 Mv 的方法,但它将一个显式的接收者作为其第一个实参;它拥有签名 func(tv T, a int) int
该函数可通过显式的接收者正常调用,因此以下调用是等价的:
t.Mv(7) f := t.Mv; f(7) T.Mv(t, 7) f1 := T.Mv; f1(t, 7)
同样,表达式 (*T).Mp 将产生一个带签名 func(tp *T, f float32) float32 的函数值来表示 Mp。 对于带值接收者的方法,它可以派生出一个带显式指针接收者的函数,因此 (*T).Mv 将产生一个带签名 func(tv *T, a int) int 的函数值来表示 Mv。