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 最被称道的能力。

posted @ 2026-11-01 20:30  sunlon  阅读(2)  评论(0)    收藏  举报