runsisi's

technical notes

go 反射

2019-03-02 runsisi#golang

Go 没有 C++ 中的多态、模板相关的概念,类似的需求都是通过 interface 实现。

当实现类似 C++ 多态时,interface 更接近于 Python 中的 duck type:

type I interface {
  Print()
}

type S struct {
}

func (s S) Print() {
  // ...
}

func useI(i I) {
  i.Print()
}

var s S = S{}
useI(s)

当实现类似 C++ 模板(更具体点应该是函数模板)时,interface 类似于 Java 中的 Object:

var v interface{}
v = 1
fmt.Println(v.(int))

如果跟 Go 自身比较,interface 类型实际上与 map、slice、chan 引用类型类似,变量自身只是一个简单的结构体,内部通过指针指向具体的存储区域。

Go 是静态类型的语言,在编译期间类型都已经确定,reflect 包存在的目的在于动态获取 interface 类型变量内部指针所指向的信息。具体而言,使用 reflect.Type 表示实际存储的数值的类型信息,使用 reflect.Value 表示实际存储的数值,相应的获取这两个信息的方法为 reflect.TypeOf(interface{}) 和 reflect.ValueOf(interface{})。

fmt.Println(reflect.TypeOf(1))
fmt.Println(reflect.ValueOf(1))

// Output:
int
1

reflect.Type 是一个 interface 类型,reflect.Value 是一个 struct 类型,对外的接口为各自的方法集以及 reflect 包中定义的一些辅助函数,其中 reflect.Value 可以通过自身的 func (Value) Type 方法返回对应的 reflect.Type 信息。

reflect.Type 和 reflec.Value 定义了一些同名的方法:

Elem, Field, FieldByIndex, FieldByName, FieldByNameFunc, Kind, Len, Method, MethodByName, NumField, NumMethod, String

需要注意的是,虽然两者定义的某些方法同名,但是方法的实现不相同,如:

Elem, Field, FieldByIndex, FieldByName, FieldByNameFunc, Method, MethodByName, String

注意 reflect.Type 与 reflect.Kind 的差异,reflect.Type.Kind() 或 reflect.Value.Kind() 返回的是一个 uint 枚举类型 reflect.Kind,其枚举值定义如下:

const (
        Invalid Kind = iota
        Bool
        Int
        Int8
        Int16
        Int32
        Int64
        Uint
        Uint8
        Uint16
        Uint32
        Uint64
        Uintptr
        Float32
        Float64
        Complex64
        Complex128
        Array
        Chan
        Func
        Interface
        Map
        Ptr
        Slice
        String
        Struct
        UnsafePointer
)

该枚举类型定义了 func (Kind) String 方法,因此在 printf 打印时会输出 Kind 枚举值所对应的字符串表示:

type S struct {
}

type I int

func main() {
a := 1
s := S{}
i := I(1)
m := map[string]int{}

fmt.Println("--- primitive:")
fmt.Println(reflect.TypeOf(a))
fmt.Println(reflect.TypeOf(a).Kind())
fmt.Println(reflect.ValueOf(a))
fmt.Println(reflect.ValueOf(a).Kind())

fmt.Println("--- struct:")
fmt.Println(reflect.TypeOf(s))
fmt.Println(reflect.TypeOf(s).Kind())
fmt.Println(reflect.ValueOf(s))
fmt.Println(reflect.ValueOf(s).Kind())

fmt.Println("--- typedef:")
fmt.Println(reflect.TypeOf(i))
fmt.Println(reflect.TypeOf(i).Kind())
fmt.Println(reflect.ValueOf(i))
fmt.Println(reflect.ValueOf(i).Kind())

fmt.Println("--- map:")
fmt.Println(reflect.TypeOf(m))
fmt.Println(reflect.TypeOf(m).Kind())
fmt.Println(reflect.ValueOf(m))
fmt.Println(reflect.ValueOf(m).Kind())

// Output:
--- primitive:
int
int
1
int
--- struct:
main.S
struct
{}
struct
--- typedef:
main.I
int
1
int
--- map:
map[string]int
map
map[]
map

此外,reflect.Value 还定义了一些类似 Float, Int, String, Uint 的方法用于返回底层实际存储的值。

更多关于 reflect 的信息可以参考 reflect 包的在线文档。

参考资料

Go Data Structures: Interfaces

https://research.swtch.com/interfaces

Part 34: Reflection

https://golangbot.com/reflection/

The Laws of Reflection

https://blog.golang.org/laws-of-reflection

Package reflect

https://golang.org/pkg/reflect/