Go 语言中的接口和反射
接口 interface
接口就是一个或多个方法的集合,若某个类型的对象实现了所有的方法,那么这个对象就实现了这个接口。
接口还可以存储值,只要这个类型的对象实现了这个接口,那么这个接口就可以存储这个类型的对象。
又因为空接口(interface{})没有一个方法,每个类型的对象至少实现了 0 个或者 0 个以上的方法,所以空接口能存储任何值。
既然接口可以被很多对象实现,那么接口就可以存储很多类型的对象或值。函数的形参就可以用接口类型,这样函数就可以接收实现了这个接口的多种类型的对象或值。
那现在有一个以接口为形参的函数,要怎么知道这个接口是什么类型的呢?
我们可以这样判断,假如函数的形参是一个接口 B 类型的值 b,我们需要知道 b 是不是 A 类型的,并且判断 A 对象是否实现了 B 接口。
我们可以使用接口中的 .(type) 来判断类型。
1 | func test(b B) { |
当然,形参也可以是空接口。
空接口可以接收任何值,一个小例子:
1 | var x float32 = 5.3 |
内嵌接口
和结构体中的匿名类型差不多。
io 包中的 Writer 和 Reader:
1 | type Reader interface { |
反射 reflection
从接口值到反射对象
类型 - 值。
Type Value。reflect.TypeOf() 获取的是 reflect.Type。reflect.ValueOf() 获取的是 reflect.Value。
1 | // TypeOf 返回 interface{} 中的值的反射类型Type |
接收参数的空接口类型(interface{}),因为任何值都有 0 个或 0 个以上的方法,可以说都实现了空接口,所以空接口可以接收任何值。
reflect 中有一个 Kind 方法,Kind() 和 TypeOf() 都是返回类型,但是又有区别。
1 | type myInt int |
会输出
1 | Type: main.myInt Kind: int |
Type 能识别自定义的类型,Kind 只能识别到底层的类型。
关于结构体的反射对象到值
一个简单的例子(取自官方文档):
1 | type T struct { |
输出
1 | 0: A int = 23 |
从反射对象到接口值
给了一个 reflect.Value。
有一个方法:
1 | func (v Value) Interface() interface{} |
例如
1 | var x int = 5 |
要修改反射对象,其值必须可设置。
1 | var x float64 = 5.4 |
调用 SetFloat() 之后会报 panic。
1 | panic: reflect: reflect.Value.SetFloat using unaddressable value |
无论是想改 x,还是 x 的副本 a,这样都无法修改。
但是如果想修改 x,可以这样:
1 | var x float64 = 5.4 |
输出
1 | c: 11.1 x: 11.1 |