
Golang 方法
方法的定义
Go 中方法和函数在形式上很像,是作用在接收器上的一个函数,接收器是某种类型的变量。因此方法是一种特殊类型的函数,只是比函数多了一个接收器,在接口中定义的函数也称为方法。
type A struct {
Face int
}
func (a A) f() {
fmt.Println("hi", a.Face)
}
接收器
接收器类型除了不能是指针类型或接口类型外,可以是其他任何类型,不仅仅是结构体类型,也可以是函数类型,还可以是以 int、bool、string 等为基础的自定义类型。
package main
import (
. "fmt"
)
type A struct {
name string
}
func (a A) print() {
Println(a.name)
}
func main() {
var a A = A {name : "A"}
b := &A {name : "B"}
// b.print() 等价于 (*b).print()
// 这是一种语法糖
b.print()
// A.print(a) 等价于 a.print()
A.print(a)
a.print()
}
程序输出
B
A
A
函数和方法的区别
方法相对于函数多了接收器,这是他们之间最大的区别。
函数是直接调用,而方法是作用在接收器上,方法需要类型的实例来调用。方法接收器必须有一个显式的名字,这个名字在方法中可以被使用。
指针方法与值方法
指针方法与值方法的区别
有类型 T 且方法的接收器为 (t T) 时称为值接收器,该方法称为值方法。方法的接收器为 (t *T) 时称为指针接收器,该方法称为指针方法。
如果想要方法改变接收器的数据,就在接收器的指针上定义该方法。否则,就在普通的值类型上定义方法,这是指针方法和值方法最大的区别。
package main
import (
. "fmt"
)
type A struct {
name string
}
func (a A) print1() {
a.name = "changed1"
}
func (a *A) print2() {
a.name = "changed2"
}
func main() {
var a A = A {name : "B"}
a.print1()
Println(a.name)
// 语法糖将值隐式转换为指针
a.print2()
Println(a.name)
}
程序输出
B
changed2
无论声明方法的接收器是指针接收器还是值接收器,Go 都可以将其隐式转换为正确的方法使用。
接口变量上的指针方法与值方法
package mian
type A struct {
name string
}
type B interface {
func1()
func2()
}
func (a A) func1() {
a.name = "A"
}
func (a *A) func2() {
a.name = "*A"
}
func main() {
var a A = A {"B"}
a.func1()
a.func2()
// 类型 A 没有实现接口 B,无法直接赋值
// cannot use b (type B) as type A in assignment: B does not implement A (func2 method has pointer receiver)
// var b B = a
var b B = &a
b.func1()
b.func2()
}
Go 有关接口与方法的规则如下
- 如果使用指针方法来实现一个接口,那么只有指向那个类型的指针才能够实现对应的接口。
- 如果使用值方法来实现一个接口,那么那个类型的值和指针都能够实现对应的接口。
方法提升规则
package main
import (
. "fmt"
)
type A struct {
name string
}
type B struct {
A
// *A
}
func (a A) func1() {
a.name = "A"
}
func (a *A) func2() {
a.name = "*A"
}
func main() {
var b B = B {A{name : "B"}}
b.func1()
b.func2()
}
程序输出
A
*A
// A
// *A
给定一个结构体 A 和 一个类型 B,Go 中匿名嵌入类型的方法集提升规则如下
- 如果 A 包含匿名字段 B,则 A 和 *A 的方法集都包含具有接收器 B 的提升方法,*A的方法集还包含具有接收器 *B 的提升方法。
- 如果 A 包含匿名字段 *B,则 A 和 *A 的方法集都包含具有接收器 B 或 *B 的提升方法。
注意:以上规则由于 A.Meth() 会被自动转换为 (&A).Meth() 这个语法糖,导致误解规则不起作用,而实际上规则是有效的。