Golang 方法
Golang 中,方法是一种作用于特定类型变量的函数,这种特定类型变量叫做:接收者( Receiver )
- ( 了解 )
也就是说,自定义类型,都可以有方法,而不仅仅是结构体- 方法与函数的区别是,函数不属于任何类型,方法属于特定的类型,即:接收者
// 基本语法 func (接收者变量 接收者类型) 方法名(参数列表) (返回参数) { 函数体 } // 接收者变量:接收者变量,在命名时,官方建议使用接收者类型名称首字母的小写,而不是self、this之类的命名 // 如,Person类型的接收者变量,应该命名为 p,Connector类型的接收者变量,应该命名为 c // 接收者类型:接收者类型和参数类似,可以是指针类型、非指针类型 // 方法名、参数列表、返回参数:具体格式与函数定义相同
为结构体添加方法 & 方法的调用
!important _ 使用值类型 / 指针类型,作为函数参数( 方法接收者 )的区别
So,什么时候应该使用指针类型接收者 ?
- 需要修改原数据中的值
- 接收者,是拷贝代价比较大的对象
- 保持一致性,如果某个方法使用了指针接收者,那么,其他的方法,也应该使用指针接收者
type Person struct { name string age int } // 为结构体添加方法 // 值类型的接收者 — 方法内的修改,不会对原对象产生影响 func (receiver Person) say1(sth string) { fmt.Println(sth) // hello // 方法接收者是值类型 receiver.age = 21 } // 指针类型的接收者 — 方法内的修改,会对原对象产生影响 // 需说明的是,是否对原数据产生影响,与方法的调用者无关,而是取决于方法接收者的类型 func (receiver *Person) say2(sth string) { fmt.Println(sth) // world receiver.age = 24 } func main() { p := Person{"小明", 20} // 方法的调用:方式 1( 常用 ) p.say1("hello") // 值类型的接收者 fmt.Println(p) // {小明 20} (&p).say2("world") // 指针类型的接收者 fmt.Println(p) // {小明 24} // 方法的调用:方式2( 拓展 )— 方法值和方法表达式( 了解 ) // 方法值 f1 := p.say2 f1("方法值") // 打印 "方法值" fmt.Println(p) // {小明 24} // 方法表达式 f2 := (*Person).say2 // 接收者类型.方法名 // 需注意的是,接收者类型,需要与方法的接收者类型一致 f2(&p, "方法表达式") // 参数,需要指定方法接收者 // 或 f3 := Person.say1 f3(p, "方法表达式") }
另,拓展 — 方法的调用:
方法值和方法表达式( 了解 )
方法的继承与重写
- Golang 中,
没有方法重载,也就是说:不同在一个包中,写名称相同的方法,即使参数不同- Golang 中,支持方法重写,也就是说:方法,是可以继承的( 方法的接收者不同 ),如果匿名字段实现了一个方法,那么,包含这个匿名字段的子结构体,也能调用该方法,甚至对该方法进行重写
换句话来说,就是:Golang 中,不允许在同一个包中,写相同的方法
- 方法名相同,即使参数不同,也视为相同的方法
- 方法接收者不同,视为不同的方法
// 父类 type Person struct { name string age int } // 子类 type Student struct { Person school string } // 父类方法 func (p *Person) walk() { fmt.Println("Person walk") } // 父类方法 func (s *Person) run() { fmt.Println("Person run") } // 子类重写父类方法 - 调用者不同,即使方法名相同,也视为不同的方法 func (s *Student) run() { fmt.Println("Student run") } func main() { p := Person{"Tom", 20} fmt.Println(p) // {Tom 20} p.walk() // Person walk s := Student{Person{"Jerry", 18}, "xxx学校"} fmt.Println(s) // {{Jerry 18} xxx学校} // (1)方法的继承:子类方法可以继承父类方法 s.walk() // Person walk p.run() // Person run // (2)方法重写:子类方法名,与父类方法名相同,优先调用子类方法 s.run() // Student run // 如果子类想调用父类方法 ... s.Person.run() // Person run }