Go 语言接口

隐式实现、接口组合、空接口、以及「接受接口返回结构体」。

Intermediate
Key PointsGo 的接口是隐式实现:方法签名对上就算实现,不需要 `implements`。

定义与实现

Go
type Shape interface {
	Area() float64
	Perimeter() float64
}

type Rect struct{ W, H float64 }

func (r Rect) Area() float64      { return r.W * r.H }
func (r Rect) Perimeter() float64 { return 2 * (r.W + r.H) }

type Circle struct{ R float64 }

func (c Circle) Area() float64      { return 3.14159 * c.R * c.R }
func (c Circle) Perimeter() float64 { return 2 * 3.14159 * c.R }

func describe(s Shape) {
	fmt.Printf("面积 %.2f 周长 %.2f\n", s.Area(), s.Perimeter())
}

describe(Rect{3, 4})
describe(Circle{1})
Output
面积 12.00 周长 14.00
面积 3.14 周长 6.28

Neither Rect nor Circle declares implements Shape. If the method set matches, they are Shape. That lets third-party types satisfy your interface without touching their code.

接口组合

Go
type Reader interface{ Read(p []byte) (int, error) }
type Writer interface{ Write(p []byte) (int, error) }

// 组合
type ReadWriter interface {
	Reader
	Writer
}

// 标准库就是这么定义的
var _ io.ReadWriter = (*os.File)(nil)   // 编译期断言

The standard library's io.ReadWriter and io.ReadWriteCloser are built this way. Keep interfaces small: one method ideally, three at most. More methods means fewer types can satisfy them.

空接口与 any

Go
// any 是 interface{} 的别名(Go 1.18+)
func printAny(v any) {
	fmt.Printf("%v (%T)\n", v, v)
}

printAny(42)
printAny("hi")
printAny([]int{1})
Output
42 (int)
hi (string)
[1] ([]int)

any holds anything but requires an assertion to get the concrete type back — giving up compile-time checking. Prefer concrete types or small interfaces in APIs.

接口的 nil 陷阱

Go
type MyErr struct{}
func (e *MyErr) Error() string { return "出错了" }

func bad() error {
	var e *MyErr = nil
	return e                 // 返回的 error 不是 nil!
}

err := bad()
fmt.Println(err == nil)    // false
fmt.Println(err)           // 出错了
Output
false
出错了
An interface value is a (type, value) pair. (*MyErr)(nil) has a non-nil type, so the interface is not nil. To return nil, **write return nil** — never return a typed variable holding nil.

接受接口,返回结构体

Go
// 好:参数用接口,调用方可以传任何实现
func WriteAll(w io.Writer, data []byte) error {
	_, err := w.Write(data)
	return err
}

// 好:返回具体类型,调用方能用到全部方法
func NewBuffer() *bytes.Buffer {
	return &bytes.Buffer{}
}

A community guideline: accept interfaces (flexible), return concrete types (complete information). Returning an interface strips type information and forces callers to assert.

标准库里值得记住的接口

InterfaceMethodMeaning
errorError() string错误
StringerString() stringfmt 打印时用它
io.ReaderRead([]byte) (int, error)可读源
io.WriterWrite([]byte) (int, error)可写目标
sort.InterfaceLen / Less / Swap可排序
http.HandlerServeHTTPHTTP 处理器
Go
type Point struct{ X, Y int }

// 实现 Stringer,fmt 就会用它
func (p Point) String() string {
	return fmt.Sprintf("(%d,%d)", p.X, p.Y)
}

fmt.Println(Point{1, 2})
Output
(1,2)

Friends & Partners