2.5 接口:空接口
空接口是不包含任何方法的接口类型。Go 里任何类型都至少实现了「方法集合为空」的接口,因此任意类型的值都可以赋给空接口变量。
从 Go 1.18 起,预定义别名 any 与 interface{} 完全等价,下文两者会并用,新代码多写 any。
概念与零值
空接口即方法集合为空的接口。书写上直接使用预定义别名 any(或 interface{})即可,一般不必再自造类型名。
接口值由动态类型与动态值两部分组成。未赋值的空接口变量二者均为 nil:
go
package main
import "fmt"
func main() {
var i any
fmt.Printf("type: %T, value: %v\n", i, i)
}text
type: <nil>, value: <nil>典型用法
1. 变量承载任意类型
go
package main
import "fmt"
func main() {
var i any
i = 1
fmt.Println(i)
i = "hello"
fmt.Println(i)
i = false
fmt.Println(i)
}2. 函数参数:单个任意类型
go
package main
import "fmt"
func printAny(v any) {
fmt.Println(v)
}
func main() {
printAny(10)
printAny("hello")
printAny(true)
}3. 变参:任意多个任意类型
go
package main
import "fmt"
func printAll(vs ...any) {
for _, v := range vs {
fmt.Println(v)
}
}
func main() {
printAll(10, "hello", true)
}4. 异构切片 []any
切片元素类型必须是同一接口类型时,常用 []interface{} / []any 存放多种动态类型(注意:这不是 []int,不能当作整型切片直接使用)。
go
package main
import "fmt"
func main() {
s := make([]any, 3)
s[0] = 11
s[1] = "hello world"
s[2] = []int{11, 22, 33}
for _, v := range s {
fmt.Println(v)
}
}注意点
1. 不能把接口值直接赋给具体类型
任意值可以放进空接口,但把已为空接口类型的值直接赋给 int、string 等具体类型不允许,必须通过类型断言或类型选择取出动态值(见 2.4 接口:类型断言)。
go
package main
func main() {
var a int = 1
var i any = a
var b int = i // 编译错误:need type assertion
_ = b
}text
cannot use i (variable of type any) as int value in assignment: need type assertion可写:b := i.(int)(确定类型时)或 b, ok := i.(int)(不确定时)。
2. 不能对 any 直接下标、切片
接口值未断言成切片类型前,编译器不知道底层是切片,i[1:3] 非法。
go
package main
func main() {
sli := []int{2, 3, 5, 7}
var i any = sli
_ = i.([]int)[1:3] // 先断言为 []int,再切片
}错误示例(勿用):
go
// var i any = []int{1, 2, 3}
// g := i[1:3] // 编译错误:cannot slice i (type interface {})3. 只知道「是 any」时,要恢复具体行为需断言
参数为 any 时,静态类型只有接口信息,要做分支或运算需 switch v := x.(type) 或 v, ok := x.(T)。
go
package main
import "fmt"
func describe(x any) {
switch x.(type) {
case int:
fmt.Println("参数类型是 int")
case string:
fmt.Println("参数类型是 string")
default:
fmt.Println("其他类型")
}
}
func main() {
describe(10)
describe("hello")
}text
参数类型是 int
参数类型是 string小结
| 点 | 说明 |
|---|---|
| 定义 | 无方法的接口;any ≡ interface{} |
| 赋值 | 任意具体值可赋给 any |
| 取出 | 赋回具体类型需类型断言,不能隐式转换 |
| 运算 | 下标、切片等需在断言为具体类型后进行 |
| 分支 | 用 type switch 或 comma-ok 断言 |
与空接口配合使用时,务必掌握 类型断言;接口设计上的常见陷阱可参考 接口的三个「潜规则」。