常用标准接口
本教程共 80 篇 · 第 46 篇 · 更新于 2026-07-27 · 约 8 分钟阅读
46. 常用标准接口
本节目标:认识 Go 标准库里最常用的几个接口,学会实现它们,让你的类型和标准库无缝配合。
fmt.Stringer:自定义打印格式
fmt.Stringer 接口只有一个方法,控制类型被 fmt 包打印时的输出:
type Stringer interface {
String() string
}
实现了 String() 方法的类型,在 fmt.Println、fmt.Printf 等函数里会自动调用它:
package main
import "fmt"
type Person struct {
Name string
Age int
}
// 实现 fmt.Stringer
func (p Person) String() string {
return fmt.Sprintf("%s(%d岁)", p.Name, p.Age)
}
func main() {
p := Person{Name: "Alice", Age: 30}
fmt.Println(p) // Alice(30岁) 自动调用 String()
fmt.Printf("%v\n", p) // Alice(30岁)
fmt.Printf("%+v\n", p) // Alice(30岁) %+v 也走 String()
fmt.Printf("%#v\n", p) // main.Person{Name:"Alice", Age:30} %#v 不走 String()
}
Tip实现了
Stringer后,fmt.Println(p)、fmt.Printf("%v", p)和%+v都会调用String()。只有%#v不会,它会用 Go 语法显示结构体的原始结构。
Warning在
String()方法里不要递归调用自己。比如fmt.Sprintf("%v", p)里p就是当前类型,会再次触发String(),导致无限递归。
error:错误接口
error 是 Go 里最重要的接口之一:
type error interface {
Error() string
}
任何实现了 Error() string 方法的类型都可以作为 error 使用:
package main
import "fmt"
// 自定义错误类型
type ValidationError struct {
Field string
Message string
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("验证失败 [%s]: %s", e.Field, e.Message)
}
func validateAge(age int) error {
if age < 0 {
return &ValidationError{
Field: "age",
Message: "年龄不能为负数",
}
}
return nil
}
func main() {
err := validateAge(-5)
if err != nil {
fmt.Println(err) // 验证失败 [age]: 年龄不能为负数
}
}
错误处理是 Go 的核心设计,后面有几章专门讲这个。
io.Reader 和 io.Writer:读写接口
io.Reader 和 io.Writer 是 Go 里最通用的 IO 接口:
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
几乎所有涉及数据读写的类型都实现了这两个接口:文件、网络连接、缓冲区、压缩流等等。因为接口统一,你可以用同一套代码处理不同的 IO 源。
来看一个实现 io.Writer 的例子──一个把写入的数据变成大写的 Writer:
package main
import (
"fmt"
"io"
"strings"
)
type UpperWriter struct {
W io.Writer
}
func (uw *UpperWriter) Write(p []byte) (n int, err error) {
upper := strings.ToUpper(string(p))
return uw.W.Write([]byte(upper))
}
func main() {
// 把 UpperWriter 包在 strings.Builder 外面
w := &UpperWriter{W: &strings.Builder{}}
w.Write([]byte("hello world"))
builder := w.W.(*strings.Builder)
fmt.Println(builder.String()) // HELLO WORLD
}
Note
io.Reader的Read方法约定:读取数据填入p,返回读取的字节数。读到末尾返回io.EOF。这个约定让所有 Reader 可以互换使用。
io.ReadWriter:组合接口
标准库用接口组合定义了 io.ReadWriter:
type ReadWriter interface {
Reader
Writer
}
文件(*os.File)就实现了 ReadWriter,既能读又能写。
sort.Interface:排序接口
要给自定义类型排序,可以实现 sort.Interface 的三个方法:
type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}
不过从 Go 1.21 开始,有了 slices.SortFunc,你不需要再实现这三个方法了。但了解 sort.Interface 还是有必要的,因为很多老代码和第三方库在用。
来看一个实现 sort.Interface 的例子:
package main
import (
"fmt"
"sort"
)
type ByAge []Person
type Person struct {
Name string
Age int
}
func (a ByAge) Len() int { return len(a) }
func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }
func (a ByAge) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func main() {
people := []Person{
{"Alice", 30},
{"Bob", 25},
{"Charlie", 35},
}
sort.Sort(ByAge(people))
fmt.Println(people)
// [{Bob 25} {Alice 30} {Charlie 35}]
}
TipGo 1.21+ 推荐用
slices.SortFunc替代sort.Sort,不需要定义新类型:
slices.SortFunc(people, func(a, b Person) int {
return cmp.Compare(a.Age, b.Age)
})
其他常用接口速览
| 接口 | 包 | 方法 | 用途 |
|---|---|---|---|
fmt.Stringer | fmt | String() | 自定义打印格式 |
error | builtin | Error() | 错误表示 |
io.Reader | io | Read() | 读取数据 |
io.Writer | io | Write() | 写入数据 |
io.Closer | io | Close() | 关闭资源 |
sort.Interface | sort | Len/Less/Swap | 排序 |
json.Marshaler | encoding/json | MarshalJSON() | 自定义JSON序列化 |
json.Unmarshaler | encoding/json | UnmarshalJSON() | 自定义JSON反序列化 |
http.Handler | net/http | ServeHTTP() | HTTP请求处理 |
小结
标准库接口是 Go 编程的基础设施:
fmt.Stringer:控制打印输出error:表示错误io.Reader/Writer:统一读写抽象sort.Interface:排序(Go 1.21+ 推荐用 slices.SortFunc)
实现这些接口能让你的类型和标准库无缝配合。下一部分进入泛型,Go 类型系统的现代化升级。