Go 语言入门系列(四):接口与错误处理——Go 的抽象方式
本篇目标:理解 Go 没有继承的世界里怎么做抽象,以及错误如何流转。这两件事决定了 Go 代码的"性格"。
一、接口与隐式实现
接口是一组方法签名。关键规则:一个类型不需要声明"我实现了某接口",只要方法集匹配,它就自动满足。
type Speaker interface {
Speak() string
}
type Dog struct{}
func (d Dog) Speak() string { return "汪汪" }
type Cat struct{}
func (c Cat) Speak() string { return "喵" }
func announce(s Speaker) {
fmt.Println(s.Speak())
}
announce(Dog{}) // 汪汪
announce(Cat{}) // 喵
Dog、Cat 都没写任何 implements 字样,但都能传给 announce。这带来一个设计优势:接口可以由使用方定义。你可以在自己的包里定义窄接口接收别人的类型,对方无需预先知道你的接口存在。
二、空接口与类型断言
any(即 interface{})不要求任何方法,所有类型都满足它:
var x any = 42
n, ok := x.(int) // 类型断言,ok 表示是否成功
if !ok {
fmt.Println("不是 int")
}
switch v := x.(type) { // 类型 switch
case int:
fmt.Println("int:", v)
case string:
fmt.Println("string:", v)
default:
fmt.Println("其他")
}
泛型出现后,很多用 any + 断言的地方有了更清晰的写法,但标准库和旧代码里仍常见。
三、常见标准库接口
error:只有一个Error() string方法fmt.Stringer:String() string,实现后fmt.Println会调用它io.Writer:Write([]byte) (int, error),文件、网络连接、缓冲区都实现了它
学会用标准库接口约束参数,代码通用性立刻提升:
func writeAll(w io.Writer, data string) error {
_, err := w.Write([]byte(data))
return err
}
传 os.File、bytes.Buffer、net.Conn 都能用。
四、错误处理
Go 没有异常。错误是普通值,作为最后一个返回值交给调用方:
func divide(a, b int) (int, error) {
if b == 0 {
return 0, errors.New("除数不能为零")
}
return a / b, nil
}
result, err := divide(10, 0)
if err != nil {
fmt.Println(err)
return
}
包装错误——用 %w 添加上下文同时保留错误链:
if err != nil {
return fmt.Errorf("读取配置失败: %w", err)
}
判断错误链——不要用 == 比较包装后的错误,用 errors.Is / errors.As:
if errors.Is(err, os.ErrNotExist) {
// 文件不存在
}
var pathErr *os.PathError
if errors.As(err, &pathErr) {
fmt.Println("路径错误:", pathErr.Path)
}
Is 判断"是不是某个错误",As 判断"能不能提取成某个类型"。
五、defer 与资源管理
defer 把调用推迟到函数返回时执行,无论正常返回还是中途 return:
f, err := os.Open("data.txt")
if err != nil {
return err
}
defer f.Close()
标准写法。注意 defer 在 return 之后按后进先出顺序执行。
六、panic 与 recover 的边界
panic 触发运行时崩溃,recover 在 defer 中捕获。它们不是错误处理机制,只适合:
- 程序无法继续的致命错误(如初始化失败)
- 框架层兜底,防止单个请求崩溃整个服务
日常业务逻辑一律用 error 返回,不要用 panic 代替。
七、小结
- 接口隐式实现,接口由使用方定义,优先窄接口
any+ 类型断言/switch 处理任意类型- 熟悉
error、Stringer、io.Writer等标准接口 - 错误是值,
%w包装,errors.Is/As判断 defer管资源,panic/recover只做兜底
练习:定义一个 Shape 接口(Area() float64),实现 Circle、Rect,写 TotalArea(shapes []Shape) float64;再让 divide 的错误支持 errors.Is 判断。
下一篇讲并发:goroutine、channel、sync 与 context——Go 最被称道的能力。

浙公网安备 33010602011771号