Files
cs-note/hzh/GolangStar/Go语言基础/Go语言接口.md
T

198 lines
5.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
tags: [go, golang, go基础语法, 接口]
create time: 2026-06-07 15:00
---
# Go 语言接口
## 概述
Go 的接口是**隐式实现**——不需要 `implements` 关键字,只要类型拥有接口声明的所有方法,就自动实现了该接口。本文覆盖接口定义、空接口、类型断言、接口嵌套等核心概念。
## 正文
### 为什么 Go 接口是"鸭嘴兽"式的?
> "如果它走起来像鸭子,叫起来像鸭子,那它就是鸭子。"
Go 接口是**行为契约**:不要求类型显式声明"我实现了这个接口",只要你的类型有对应的方法签名,就默认实现。这种设计让新类型可以天然适配已有接口。
```go
type Reader interface {
Read(p []byte) (n int, err error)
}
// 任何有 Read([]byte) (int, error) 签名的类型都是 Reader
// 无需 implements 或 extends
```
> [!question] ❓ 思考
> 如果一个结构体有 `Read` 方法但没声明过要实现 `Reader` 接口,它能被接收 `Reader` 参数的函数调用吗?(答案:能!)
### 接口定义与实现
```go
type Phone interface {
Call()
SendMessage()
}
type Apple struct{ Name string }
func (a Apple) Call() { fmt.Println(a.Name, "打电话") }
func (a Apple) SendMessage() { fmt.Println(a.Name, "发短信") }
type Huawei struct{ Name string }
func (h Huawei) Call() { fmt.Println(h.Name, "打电话") }
func (h Huawei) SendMessage() { fmt.Println(h.Name, "发短信") }
// Apple 和 Huawei 都自动实现了 Phone 接口
var p Phone = Apple{"iPhone"}
p.Call() // iPhone 打电话
p = Huawei{"Mate"}
p.SendMessage() // Mate 发短信
```
#### 多接口实现
一个类型可以实现多个接口:
```go
type Reader interface { Read() }
type Writer interface { Write() }
type ReadWriter struct{}
func (rw ReadWriter) Read() {}
func (rw ReadWriter) Write() {}
// ReadWriter 同时实现了 Reader 和 Writer
```
### 空接口 `interface{}`
没有方法的接口就是空接口——**所有类型都实现了它**。
```go
var any interface{}
any = 42 // int
any = "hello" // string
any = true // bool
// fmt.Println 的参数就是 ...interface{}
fmt.Println(any, 3.14, "world")
```
> [!info] ℹ️ Go 1.20+ 的 `any` 别名
> `any` 是 `interface{}` 的内置别名,语义等价但更简洁:
> ```go
> func Process(v any) { ... }
> ```
### 类型断言
从空接口中取出原始值需要**类型断言**:
```go
x := getValue() // 返回 interface{}
// 方式一:带 ok 判断(推荐)
val, ok := x.(int)
if ok {
fmt.Println("是 int:", val)
} else {
fmt.Println("不是 int")
}
// 方式二:直接取值(失败会 panic)
val := x.(int) // ❌ 如果 x 不是 int,程序崩溃
```
> [!warning] ⚠️ 忘记 ok 的危险
> `x.(int)` 在类型不匹配时会 **panic**。生产代码中始终使用 `val, ok := x.(T)` 形式。
#### switch 类型断言
当不确定具体类型时,用 `switch` + `.(type)` 处理多种可能:
```go
func describe(i interface{}) {
switch v := i.(type) {
case int:
fmt.Printf("int: %d\n", v)
case string:
fmt.Printf("string: %s\n", v)
case bool:
fmt.Printf("bool: %t\n", v)
default:
fmt.Printf("unknown: %T\n", i) // %T 打印动态类型
}
}
describe(42) // int: 42
describe("hi") // string: hi
describe(nil) // unknown: <nil>
```
> [!note] 📝 nil interface vs interface(nil)
> - `var x interface{} = nil` → `x == nil` 为 true
> - `var x interface{} = (*int)(nil)` → `x != nil`(类型信息存在,只是值为 nil)
### 接口作函数参数
这是 Go 中最常见的接口用法——让函数接受"行为"而非"具体类型":
```go
type Speaker interface {
Speak() string
}
func Greet(s Speaker) {
fmt.Println("Hello,", s.Speak())
}
type Dog struct{ Name string }
func (d Dog) Speak() string { return d.Name }
type Human struct{ Name string }
func (h Human) Speak() string { return h.Name }
Greet(Dog{"Rex"}) // Hello, Rex
Greet(Human{"Alice"}) // Hello, Alice
```
> [!tip] 💡 依赖倒置
> 函数应该依赖接口而非具体类型。这样新增类型时无需修改现有函数代码——符合开闭原则。
### 接口嵌套
接口可以嵌入其他接口,组合出更大的契约:
```go
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
// Closer 组合了 Reader 和 Writer 的所有方法
type Closer interface {
Reader
Writer
Close() error
}
// 实现 Closer 需要实现 Read/Write/Close 三个方法
```
> [!info] ℹ️ io 包的经典嵌套
> `io.ReadCloser`、`io.WriteCloser`、`io.ReadWriteCloser` 都是 stdlib 中嵌套接口的实际应用。
### 常见陷阱总结
| 陷阱 | 说明 | 修复 |
|------|------|------|
| nil 接口 vs 含 nil 值的接口 | `(*T)(nil)` 赋值给接口后不为 nil | 注意区分 |
| 类型断言不加 ok | 类型不匹配会 panic | 始终用 `v, ok := x.(T)` |
| 接口不能作为 map key | 接口类型不可比较(除非底层类型可比较) | 用其他字段做 key |
| 空接口失去类型安全 | 取回时需要断言 | Go 1.20+ 用泛型替代部分场景 |