Skip to content

2.5 接口:空接口

空接口是不包含任何方法的接口类型。Go 里任何类型都至少实现了「方法集合为空」的接口,因此任意类型的值都可以赋给空接口变量。

从 Go 1.18 起,预定义别名 anyinterface{} 完全等价,下文两者会并用,新代码多写 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. 不能把接口值直接赋给具体类型

任意值可以放进空接口,但把已为空接口类型的值直接赋给 intstring 等具体类型不允许,必须通过类型断言类型选择取出动态值(见 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

小结

说明
定义无方法的接口;anyinterface{}
赋值任意具体值可赋给 any
取出赋回具体类型需类型断言,不能隐式转换
运算下标、切片等需在断言为具体类型后进行
分支type switchcomma-ok 断言

与空接口配合使用时,务必掌握 类型断言;接口设计上的常见陷阱可参考 接口的三个「潜规则」