接口
约 898 字大约 3 分钟
2026-01-18
1. 基本概念
接口(interface)是方法集合,类型只要实现了接口中声明的全部方法,就被视为实现了该接口(隐式实现,无需关键字)。接口本身的零值是 nil。
type Speaker interface {
Speak() string
}2. 实现接口
方法集满足即视为实现:无需显式声明“implements”。值接收者的方法自动满足接口;若接口方法是指针接收者,则需要用指针类型赋值。
隐式实现与指针/值接收者差异
package main
import "fmt"
type Speaker interface {
Speak() string
}
type Person struct {
Name string
}
// 值接收者,实现 Speak
func (p Person) Speak() string {
return "hi, I'm " + p.Name
}
type Counter int
// 指针接收者,实现 Speak
func (c *Counter) Speak() string {
*c++
return fmt.Sprintf("count=%d", *c)
}
func main() {
var s Speaker
s = Person{Name: "Bob"} // 值可赋给接口
fmt.Println(s.Speak())
var c Counter = 1
// s = c // 编译错误:Counter 的值方法集不含指针接收者方法
s = &c // 用指针满足接口
fmt.Println(s.Speak())
}重要
- 值接收者的方法集可通过值或指针调用;指针接收者的方法集只能由指针调用。
- 将实现接口的类型赋给接口变量时,会产生一次复制(接口内部保存具体类型和值)。
3. 接口作为参数
通过接口抽象行为,调用方只依赖接口,不关心具体实现。
接口作为参数
package main
import "fmt"
type Writer interface {
Write(msg string)
}
type Logger struct{}
func (Logger) Write(msg string) { fmt.Println("log:", msg) }
func handle(w Writer) {
w.Write("hello")
}
func main() {
handle(Logger{})
}4. 类型断言与类型分支
接口值可通过类型断言还原具体类型;断言失败会 panic,可使用“逗号 ok”形式避免。
类型断言与 type switch
package main
import "fmt"
func printAny(v any) {
// 断言 + ok 防 panic
if s, ok := v.(string); ok {
fmt.Println("string:", s)
}
// 类型分支
switch val := v.(type) {
case int:
fmt.Println("int:", val)
case fmt.Stringer:
fmt.Println("stringer:", val.String())
default:
fmt.Printf("other: %#v\n", val)
}
}
type User struct{ Name string }
func (u User) String() string { return "User(" + u.Name + ")" }
func main() {
printAny(42)
printAny("hi")
printAny(User{Name: "Alice"})
}5. 接口嵌入
接口可以嵌入其他接口,组成更大的方法集。
type Reader interface { Read() error }
type Writer interface { Write() error }
type ReadWriter interface {
Reader
Writer
}6. 接口与 nil
- 接口的零值是
nil,但只有“动态类型”和“动态值”都为nil才等于nil。 - 常见陷阱:将
(*T)(nil)赋给接口,接口本身非nil,if iface == nil会是 false。
接口 nil 陷阱
package main
import "fmt"
type Err interface {
Error() string
}
type MyErr struct{}
func (MyErr) Error() string { return "oops" }
func returnsNil() Err {
var e *MyErr = nil
return e // 接口内保存了类型 *MyErr 和 nil 值 => 接口本身非 nil
}
func main() {
if v := returnsNil(); v == nil {
fmt.Println("nil interface") // 不会进入
} else {
fmt.Println("not nil:", v)
}
}7. 接口 vs 结构体
- 角色不同:结构体用于承载数据(字段的组合);接口用于抽象行为(方法集合)。
- 实现方式:结构体显式定义字段;接口由任意类型通过实现全部方法“隐式满足”,无需关键字。
- 实例化与零值:结构体实例化后有具体字段值,零值是各字段的零值;接口的零值是
nil,需要绑定到实现类型的值或指针才可调用方法。 - 复制语义:将结构体值赋给接口会复制一份值存入接口内部;如需共享可变状态,可用结构体指针赋给接口。
- 行为扩展:结构体通过组合/嵌入复用字段和方法;接口可通过接口嵌入叠加方法集。