一、Go 方法核心概念
1. 方法本质
方法本质是函数 ,但必须和特定类型绑定 (区别于普通函数),是 Go 实现面向对象++封装++的核心手段。
2. 函数 vs 方法 语法对比
go
Go
// 1. 普通函数(无类型绑定)
func 函数名(参数) 返回值 {}
// 2. 匿名函数
func() {}
// 3. 方法(绑定指定类型,必须有接收者)
func (接收者变量 绑定类型) 方法名(参数) 返回值 {}
- 接收者:相当于方法的第一个参数,是绑定类型的实例
- 只有绑定了类型的函数,才能叫方法
3. 方法的意义:实现封装
普通函数没有 "归属",方法可以给类型绑定专属函数,就像机器的按钮,隐藏内部实现,对外提供操作接口。
二、方法基础使用
1. 给自定义类型绑定方法
go
Go
package main
import "fmt"
// 自定义类型
type long int
// 普通函数:面向过程
func Add01(a, b long) long {
return a + b
}
// 方法:给long类型绑定,面向对象
// tmp:接收者,相当于参数
func (tmp long) Add02(other long) long {
return tmp + other
}
func main() {
// 调用普通函数
res1 := Add01(1, 1)
fmt.Println("函数调用:", res1)
// 调用方法:变量名.方法名()
var a long = 2
res2 := a.Add02(3)
fmt.Println("方法调用:", res2)
}
核心:面向对象只是换了一种调用方式,本质还是函数。
2. 给结构体类型绑定方法(最常用)
结构体是 Go 中最常用的绑定类型,可实现属性 + 方法的封装:
go
Go
type Person struct {
name string
sex byte
age int
}
// 绑定打印方法
func (tmp Person) PrintInfo() {
fmt.Println("成员信息:", tmp)
}
// 绑定赋值方法
func (p *Person) SetInfo(n string, s byte, a int) {
p.name = n
p.sex = s
p.age = a
}
func main() {
// 初始化结构体
p := Person{"mike", 'm', 18}
p.PrintInfo() // 调用方法
// 指针接收者方法调用
var p2 Person
(&p2).SetInfo("yoyo", 'f', 22)
p2.PrintInfo()
}
三、方法使用规则
1. 接收者类型限制
- 接口类型不能作为接收者
- 接收者的基础类型本身不能是指针
go
Go
// 错误写法:类型本身是指针,不能做接收者
type pointer *int
func (tmp pointer) test() {}
// 正确写法:接收者是指针类型(推荐)
type long int
func (tmp *long) test02() {}
2. 不支持重载,支持同名方法
Go不支持函数重载 ,但接收者类型不同,同名方法不算重载,是独立方法:
go
Go
type long int
type char string
// 两个test方法,接收者不同,相互独立
func (tmp long) test() {}
func (tmp char) test() {}
四、值接收者 vs 指针接收者(核心重点)
1. 本质区别:值传递 vs 指针传递
| 类型 | 传递方式 | 效果 | 使用场景 |
|---|---|---|---|
| 值接收者 | 值拷贝 | 方法内修改,不影响原变量 | 只读操作、小型结构体 |
| 指针接收者 | 引用传递 | 方法内修改,直接修改原变量 | 修改成员、大型结构体、提升效率 |
2. 代码示例
go
Go
type Person struct {
name string
age int
}
// 值接收者:值传递,拷贝副本
func (p Person) SetInfoValue(n string, a int) {
p.name = n
p.age = a
}
// 指针接收者:引用传递,操作原变量
func (p *Person) SetInfoPointer(n string, a int) {
p.name = n
p.age = a
}
func main() {
s1 := Person{"go", 22}
// 值接收者:不修改原变量
s1.SetInfoValue("mike", 18)
fmt.Println(s1) // 输出:{go 22}
// 指针接收者:修改原变量
(&s1).SetInfoPointer("mike", 18)
fmt.Println(s1) // 输出:{mike 18}
}
五、方法集(自动类型转换)
Go 编译器会自动转换接收者类型,无需严格遵守方法集约束:
- 指针类型变量,可以调用值接收者 + 指针接收者方法
- 普通类型变量,可以调用值接收者 + 指针接收者方法
go
Go
type Person struct {
name string
}
func (p Person) SetInfoValue() {} // 值接收者
func (p *Person) SetInfoPointer() {} // 指针接收者
func main() {
// 指针变量:调用两种方法
p1 := &Person{"mike"}
p1.SetInfoValue()
p1.SetInfoPointer()
// 普通变量:调用两种方法
p2 := Person{"mike"}
p2.SetInfoValue()
p2.SetInfoPointer() // 编译器自动转换为 (&p2).SetInfoPointer()
}
六、方法的继承与重写
1. 方法继承:通过匿名字段实现
结构体嵌套匿名结构体,自动继承匿名结构体的所有成员和方法:
go
Go
// 父结构体
type Person struct {
name string
age int
}
// 父结构体方法
func (tmp *Person) PrintInfo() {
fmt.Printf("姓名:%s,年龄:%d\n", tmp.name, tmp.age)
}
// 子结构体:嵌套Person(匿名字段),实现继承
type Student struct {
Person // 匿名字段
id int
addr string
}
func main() {
s := Student{Person{"mike", 18}, 666, "北京"}
s.PrintInfo() // 直接调用继承的方法
}
2. 方法重写:就近原则
子结构体定义和父结构体同名的方法,就是方法重写:
- 调用时:优先调用自身方法(就近原则)
- 强制调用父类方法:
子变量.父结构体.方法()
go
Go
// 子结构体重写方法
func (tmp *Student) PrintInfo() {
fmt.Println("学生信息:", tmp)
}
func main() {
s := Student{Person{"mike", 18}, 666, "北京"}
// 就近原则:调用子结构体方法
s.PrintInfo()
// 显式调用父结构体方法
s.Person.PrintInfo()
}
七、方法值 & 方法表达式
1. 方法值
将方法赋值给变量,隐藏接收者,直接调用变量即可:
go
Go
func main() {
p := Person{"mike", 18}
// 传统调用
p.SetInfoPointer()
// 方法值:绑定接收者,直接调用
pFunc := p.SetInfoPointer
pFunc() // 等价于 p.SetInfoPointer()
vFunc := p.SetInfoValue
vFunc()
}
2. 方法表达式
显式传递接收者,格式:(类型).方法名:
go
Go
func main() {
p := Person{"mike", 18}
// 指针接收者方法表达式
f1 := (*Person).SetInfoPointer
f1(&p) // 显式传接收者
// 值接收者方法表达式
f2 := (Person).SetInfoValue
f2(p)
}
八、核心总结
- 方法 = 绑定类型的函数,是 Go 封装的核心,接收者是方法第一个参数
- 值接收者 :拷贝副本,不修改原变量;指针接收者:引用传递,修改原变量
- 编译器自动转换接收者类型,方法集无严格调用限制
- 匿名字段实现方法继承 ,同名方法实现方法重写(就近原则)
- 方法值 / 表达式:简化方法调用,灵活使用