GO_面向对象_方法

一、Go 方法核心概念

1. 方法本质

方法本质是函数,但必须和特定类型绑定(区别于普通函数),是 Go 实现面向对象封装的核心手段。

2. 函数 vs 方法 语法对比

go

// 1. 普通函数(无类型绑定) func 函数名(参数) 返回值 {} // 2. 匿名函数 func() {} // 3. 方法(绑定指定类型,必须有接收者) func (接收者变量 绑定类型) 方法名(参数) 返回值 {}
  • 接收者:相当于方法的第一个参数,是绑定类型的实例
  • 只有绑定了类型的函数,才能叫方法

3. 方法的意义:实现封装

普通函数没有 "归属",方法可以给类型绑定专属函数,就像机器的按钮,隐藏内部实现,对外提供操作接口。


二、方法基础使用

1. 给自定义类型绑定方法

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

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. 接收者类型限制

  1. 接口类型不能作为接收者
  2. 接收者的基础类型本身不能是指针

go

// 错误写法:类型本身是指针,不能做接收者 type pointer *int func (tmp pointer) test() {} // 正确写法:接收者是指针类型(推荐) type long int func (tmp *long) test02() {}

2. 不支持重载,支持同名方法

Go不支持函数重载,但接收者类型不同,同名方法不算重载,是独立方法:

go

type long int type char string // 两个test方法,接收者不同,相互独立 func (tmp long) test() {} func (tmp char) test() {}

四、值接收者 vs 指针接收者(核心重点)

1. 本质区别:值传递 vs 指针传递

类型传递方式效果使用场景
值接收者值拷贝方法内修改,不影响原变量只读操作、小型结构体
指针接收者引用传递方法内修改,直接修改原变量修改成员、大型结构体、提升效率

2. 代码示例

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 编译器会自动转换接收者类型,无需严格遵守方法集约束:

  1. 指针类型变量,可以调用值接收者 + 指针接收者方法
  2. 普通类型变量,可以调用值接收者 + 指针接收者方法

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

// 父结构体 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

// 子结构体重写方法 func (tmp *Student) PrintInfo() { fmt.Println("学生信息:", tmp) } func main() { s := Student{Person{"mike", 18}, 666, "北京"} // 就近原则:调用子结构体方法 s.PrintInfo() // 显式调用父结构体方法 s.Person.PrintInfo() }

七、方法值 & 方法表达式

1. 方法值

将方法赋值给变量,隐藏接收者,直接调用变量即可:

go

func main() { p := Person{"mike", 18} // 传统调用 p.SetInfoPointer() // 方法值:绑定接收者,直接调用 pFunc := p.SetInfoPointer pFunc() // 等价于 p.SetInfoPointer() vFunc := p.SetInfoValue vFunc() }

2. 方法表达式

显式传递接收者,格式:(类型).方法名

go

func main() { p := Person{"mike", 18} // 指针接收者方法表达式 f1 := (*Person).SetInfoPointer f1(&p) // 显式传接收者 // 值接收者方法表达式 f2 := (Person).SetInfoValue f2(p) }

八、核心总结

  1. 方法 = 绑定类型的函数,是 Go 封装的核心,接收者是方法第一个参数
  2. 值接收者:拷贝副本,不修改原变量;指针接收者:引用传递,修改原变量
  3. 编译器自动转换接收者类型,方法集无严格调用限制
  4. 匿名字段实现方法继承,同名方法实现方法重写(就近原则)
  5. 方法值 / 表达式:简化方法调用,灵活使用