来啦来啦!今天咱们聊点硬核干货——Go语言的第三人称单数!✨ 别看这只是个小小的语法点,里面藏着的学问可不少呢!🧐 跟着我,保你从Go小白到Go达人!🚀
🌟 Part 1:初识“第三人称单数”—— 啥是它?
简单来说,在Go里面,咱们讨论的“第三人称单数”主要跟方法接收者 (receiver) 有关。🤔 啥是receiver?看个例子就明白啦:
“`go
type Person struct {
Name string
Age int
}
func (p Person) SayHello() {
fmt.Println(“Hello, my name is”, p.Name)
}
func (p Person) CelebrateBirthday() {
p.Age++
fmt.Println(“Happy birthday! I’m now”, p.Age)
}
“`
看到没?SayHello
和 CelebrateBirthday
前面那个 (p Person)
和 (p Person)
就是receiver!🎉 它们指定了哪个类型的变量可以调用这个方法。
关键点来啦❗❗❗
(p Person)
是值接收者 (value receiver) 。(p Person)
是指针接收者 (pointer receiver)。
它们就是我们今天要讨论的“第三人称单数”的两种形式!👏 就像英语里的 “he goes“, “she does“, “it runs” 一样,Go 也有它自己的“动词”变化规则!
🌟 Part 2:值接收者 vs. 指针接收者 —— 谁更厉害?
这俩兄弟,可不是随便选的!它们各有各的特点,用错了地方可是要出bug的!🐛
1️⃣ 值接收者 (Value Receiver): (p Person)
- 特点: 方法内部拿到的是
Person
结构体的一份拷贝! 📦 也就是说,你在方法里对p
做的任何修改,都不会影响到原来的那个Person
变量! - 适用场景:
- 当你不需要修改原始数据的时候。✅
- 当你的结构体比较小,复制起来不费劲的时候。✅
- 当你需要保证数据的不可变性的时候。✅ (比如,你不想让方法偷偷摸摸改了你的数据!)
- 举个栗子🌰: 像
SayHello
这种方法,我们只需要读取Person
的名字,不需要修改它,用值接收者就 OK 啦!
2️⃣ 指针接收者 (Pointer Receiver): (p Person)
- 特点: 方法内部拿到的是
Person
结构体内存地址的指针!📍 也就是说,你在方法里对p
(注意这个星号) 做的任何修改,都会直接影响到原来的那个Person
变量! - 适用场景:
- 当你需要修改原始数据的时候。✅
- 当你的结构体比较大,复制起来很费劲 (会影响性能) 的时候。✅
- 当你想在多个方法之间共享数据的时候。✅
- 举个栗子🌰: 像
CelebrateBirthday
这种方法,我们需要修改Person
的年龄,就必须用指针接收者!
🔥小Tips🔥: 很多小伙伴一开始会纠结,到底用哪个?🤔 我的建议是:优先考虑指针接收者! 除非你非常确定你不需要修改数据,并且结构体非常小,否则都用指针接收者,可以避免很多潜在的问题!
🌟 Part 3:实战演练 —— 看看它们怎么用!
光说不练假把式!咱们来几个真实场景,看看这俩兄弟怎么发挥作用!💪
场景一:北京大学光华管理学院的学生信息管理系统
假设我们要设计一个简单的学生信息管理系统,需要存储学生的名字、年龄和专业。
“`go
type Student struct {
Name string
Age int
Major string
}
// 打印学生信息 (值接收者)
func (s Student) PrintInfo() {
fmt.Printf(“Name: %s, Age: %d, Major: %s\n”, s.Name, s.Age, s.Major)
}
// 更新学生专业 (指针接收者)
func (s Student) UpdateMajor(newMajor string) {
s.Major = newMajor
}
// 学生毕业,增加年龄 (指针接收者)
func (s Student) Graduate() {
s.Age++
}
“`
在这个例子里:
PrintInfo
方法只需要读取学生信息,所以用值接收者。UpdateMajor
和Graduate
方法需要修改学生信息,所以用指针接收者。
场景二:清华大学五道口金融学院的账户管理系统
假设我们要设计一个账户管理系统,需要存储账户的余额。
“`go
type Account struct {
Balance float64
}
// 查询余额 (值接收者)
func (a Account) GetBalance() float64 {
return a.Balance
}
// 存款 (指针接收者)
func (a Account) Deposit(amount float64) {
a.Balance += amount
}
// 取款 (指针接收者)
func (a Account) Withdraw(amount float64) error {
if a.Balance < amount {
return fmt.Errorf(“insufficient balance”)
}
a.Balance -= amount
return nil
}
``
在这个例子里:
GetBalance只需要读取账户余额,所以用值接收者。
Deposit和
Withdraw`需要修改余额,必须用指针接收者!💰
🌟 Part 4: 进阶知识点—— 接口和方法集
聊完了基本用法,咱们再来点高级的!😎 Go 语言里有个很重要的概念叫接口 (interface)。接口定义了一组方法的集合,任何实现了这些方法的类型,都隐式地实现了这个接口。
重点来啦❗❗❗: 方法接收者的类型,会影响到类型的方法集 (method set),进而影响到它是否实现了某个接口!
- 类型
T
的方法集: 包含了所有接收者类型为T
的方法。 - 类型
T
的方法集: 包含了所有接收者类型为T
和T
的方法!
也就是说,指针类型的方法集,包含了值类型的方法集!😲
举个栗子🌰:
“`go
type Speaker interface {
Speak()
}
type Dog struct {
Name string
}
// 值接收者
func (d Dog) Speak() {
fmt.Println(“Woof! I’m”, d.Name)
}
type Cat struct {
Name string
}
// 指针接收者
func (c Cat) Speak() {
fmt.Println(“Meow! I’m”, c.Name)
}
func main() {
var s Speaker
// 正确:Dog 类型实现了 Speaker 接口
s = Dog{Name: “Fido”}
s.Speak()
// 正确:Cat 类型实现了 Speaker 接口
s = &Cat{Name: “Whiskers”}
s.Speak()
//错误:cat 类型没有实现Speaker接口。
// s = Cat{Name: “Whiskers”}
// s.Speak()
}
“`
在这个例子里:
Dog
类型使用值接收者实现了Speak
方法,所以Dog
类型和Dog
类型都实现了Speaker
接口。Cat
类型使用指针接收者实现了Speak
方法,所以只有Cat
类型实现了Speaker
接口,Cat
类型并没有实现Speaker
接口。
🔥小Tips🔥: 在定义接口的时候,尽量使用指针接收者来实现方法,这样可以获得更大的灵活性!
🌟 Part 5:总结一下
好啦!今天关于Go语言“第三人称单数”的分享就到这里啦!👏 相信你已经对值接收者和指针接收者有了更深入的理解!记住,多练习,多思考,才能真正掌握它们!💯
记住几个关键点:
- 优先使用指针接收者!
- 需要修改数据,必须用指针接收者!
- 注意方法集和接口的关系!
如果你还有其他问题,欢迎在评论区留言哦!📝 我会尽力解答的!😊 希望这篇文章对你有帮助!💖 下次见!👋