Go 接口定义了一组方法签名——一份行为契约。关键是,接口是隐式满足的:任何具有这些方法的类型都会自动满足该接口,无需显式的 "implements" 声明。这种结构化的鸭子类型方法既独特又强大。
定义和隐式实现接口
go
Speaker {
Speak()
}
Dog {}
Speak() { }
Cat {}
Speak() { }
s Speaker = Dog{}
s.Speak()
没有 implements 关键字——如果一个类型拥有接口的所有方法,它就自动满足该接口。这将接口与其实现解耦(实现者甚至不需要知道接口的存在)。
func announce(s Speaker) { // accepts ANY type that has Speak()
fmt.Println(s.Speak())
}
announce(Dog{}) // "Woof"
announce(Cat{}) // "Meow"
speakers := []Speaker{Dog{}, Cat{}} // a slice of different types via the interface
for _, sp := range speakers { sp.Speak() }
接口支持多态——针对行为编程,任何符合条件的类型都能工作。
anyvar x interface{} // the empty interface — satisfied by EVERY type (no methods required)
x = 42
x = "hello"
// Go 1.18+: `any` is an alias for interface{}
func print(v any) { fmt.Println(v) } // accepts anything
空接口(interface{} / any)可以保存任何值——在编译时类型未知时使用(虽然现在泛型通常会替代这个做法)。
type Stringer interface { String() string } // controls fmt printing
type error interface { Error() string } // the error type itself!
type io.Reader interface { Read(p []byte) (n int, err error) }
type io.Writer interface { Write(p []byte) (n int, err error) }
标准库的大部分基于小接口(io.Reader/io.Writer、error、Stringer)——实现它们可以让你的类型与整个生态系统配合工作。
Go favors SMALL interfaces (often one method) defined where they're USED (the consumer),
not where types are defined. "The bigger the interface, the weaker the abstraction."
接口是 Go 设计的核心,代表了其抽象和多态的方法——而它们的隐式满足(无 implements 关键字;结构化类型)是该语言最独特和最强大的特性之一。
这种解耦意味着类型和接口可以独立演进,你可以在满足接口的类型之后定义接口,任何符合条件的类型都会自动插入基于接口的代码中——实现灵活、松耦合的设计和简化的测试(可以轻松替换为 mock 实现)。
理解隐式实现、通过接口的多态、空接口/any、小接口习用法("接受接口,返回结构体")以及无处不在的标准库接口(error、io.Reader/Writer、Stringer)对于习惯用法的 Go 开发是必不可少的。
这是一个常见的面试话题,恰恰因为隐式的结构化模型与 Java/C# 等语言的显式接口差异甚大。