Golang 从入门到放弃 -0x09
方法、值接收者、指针接收者、接口、类型断言与 any 的基本用法。
上一章讲了 struct,这一章把它和方法、接口串起来。Go 没有 class,没有继承,初看会觉得它好像没什么“面向对象味道”。但真写起业务来你会发现,struct + method + interface 这三件套,已经能撑起很多工程结构。
方法:就是长在类型上的函数
普通函数大家已经熟了,方法就是在函数前面多挂一个接收者。
type Counter struct {
Num int
}
func (c Counter) Value() int {
return c.Num
}这里的 (c Counter) 就是接收者,意思是这个函数属于 Counter 这种类型。
counter := Counter{Num: 10}
fmt.Println(counter.Value())值接收者和指针接收者
这是方法里最常见的一道坎。区别很简单:
- 值接收者拿到的是副本
- 指针接收者拿到的是原对象地址
func (c Counter) AddWrong() {
c.Num++
}
func (c *Counter) Add() {
c.Num++
}counter := Counter{Num: 10}
counter.AddWrong()
fmt.Println(counter.Num) // 10
counter.Add()
fmt.Println(counter.Num) // 11如果你的方法需要修改结构体本身,基本就该用指针接收者了。
接口:只关心你会不会,不关心你是谁
接口定义的是一组行为,而不是具体数据。
type Speaker interface {
Speak() string
}任何类型,只要实现了 Speak() string 这个方法,就算实现了这个接口。注意,Go 不需要你显式写什么 implements,它是隐式实现的。
type Dog struct{}
func (Dog) Speak() string {
return "wang"
}
type Cat struct{}
func (Cat) Speak() string {
return "miao"
}func makeSound(s Speaker) {
fmt.Println(s.Speak())
}
makeSound(Dog{})
makeSound(Cat{})这种设计很灵活。函数不需要知道你是狗还是猫,只需要知道你会叫。
接口的价值
接口最大的意义,是解耦。你写业务逻辑时,可以依赖行为,而不是死死绑住某个具体实现。
比如“发消息”这件事,后面你可以有 EmailSender、SmsSender、WxSender。业务代码只认 Send(string) error 这个接口,至于你底层是走 SMTP 还是第三方平台,它不关心。
空接口和 any
以前大家会写 interface{},现在你更常看到 any。本质上它们是一回事,表示“我先什么都能接”。
func printValue(v any) {
fmt.Println(v)
}这很灵活,但也意味着类型信息变弱了。所以能具体一点的时候,还是尽量具体一点。别把所有参数都做成 any,那会把静态语言的好处挥霍掉。
类型断言
当你拿到一个接口值,想知道它里面到底装的是谁,可以用类型断言。
var v any = "hello"
str, ok := v.(string)
if ok {
fmt.Println("字符串长度:", len(str))
}也可以配合 switch 做类型判断。
func showType(v any) {
switch value := v.(type) {
case int:
fmt.Println("int:", value)
case string:
fmt.Println("string:", value)
default:
fmt.Println("unknown")
}
}小结
这一章比较重要,因为它关系到你以后怎么组织代码:
- 方法就是挂在类型上的函数。
- 要改原对象,多半用指针接收者。
- 接口描述行为,Go 用隐式实现。
any很方便,但别滥用。
下一章我们讲包和模块。写到那里,Go 项目的骨架就开始真正成型了。