6. 接口与错误处理
这是什么:接口(interface) = 只列方法名的能力契约;error = Go 内置的错误类型,所有失败都用返回值表示。
为什么用:上一章的 struct 靠字段名硬绑,想让不同类型干同一件事只能挨个判断类型;出错用 panic 兜底,容易整段崩掉。接口让代码只认「会不会某件事」,error 让失败沿直线传回调用方。
如何用:先定义只列 Speak() 的 Speaker 接口,让 Dog 和 Robot 各自实现,再写一个返回 error 的函数,用 if err != nil 接住失败。
6.1 接口:一纸能力契约
type Speaker interface {
Speak() string // 只要你有 Speak() 你就是 Speaker
}
隐式实现(不用声明 implements):
type Dog struct{}
func (d Dog) Speak() string { return "汪" }
type Robot struct{}
func (r Robot) Speak() string { return "哔" }
// 都满足 Speaker,可统一处理
func makeSound(s Speaker) {
fmt.Println(s.Speak())
}
makeSound(Dog{}) // 汪
makeSound(Robot{}) // 哔
隐式接口 = 「长得像,就当是」。标准库
io.Writer、fmt.Stringer全靠这招贯穿生态。
6.2 error 是接口不是异常
Go 没有 try/catch——所有可失败操作返回 error,代码像直线:
f, err := os.Open("missing.txt")
if err != nil {
fmt.Println("打开失败:", err)
return // 或返回 err 一路上抛
}
defer f.Close() // 成功才注册收尾
造一个错误:
errors.New("意外输入")
fmt.Errorf("值 %d 不合法", n) // 带格式 + 可包内层错误 %w(进阶)
自定义错误类型(承载更多信息):
type NotFound struct{ Key string }
func (e NotFound) Error() string { return "找不到:" + e.Key }
金句:错误是值。看得见流向、写起测试简单,也是 Go 代码「if err != nil」铺地面的原因。
6.3 panic 与 recover(慎用)
panic:程序不可继续的致命错误(如数组彻底越界)recover:在 defer 中捞回 panic,一般只在最外层兜底(Web 框架替你做了)
defer func() {
if r := recover(); r != nil {
fmt.Println("救回:", r)
}
}()
规矩:业务错误用 error,系统崩溃才 panic。
6.4 判断与断言
var s Speaker = Dog{}
d, ok := s.(Dog) // 类型断言:s 是 Dog 吗
if ok { fmt.Println(d.Breed) }
// switch 版
switch v := s.(type) {
case Dog: fmt.Println("狗", v)
case Robot: fmt.Println("机器人")
}
6.5 动手改
- 写
Animal泛型 Speaker 实现(Cat、Robot),slot makeSound - divide 函数换成自定义错误「除数不能为 0」,调用方别忘了检查
- 手造 panic 并 recover 观察
6.6 小结
- 接口 = 方法签名清单,隐式实现一词两环境
- 一切错误是值:
(res, err)+ 检查和向上传递 - panic 保命、recover 清理,平时同 error
下一章:并发:goroutine 与 channel 🐸