📌

Go の reflect パッケージ頻出関数めも

2021/12/29に公開

TypeOf(interface{}) reflect.Type

引数に与えられた値の型情報を取得する。取得結果は Type というインタフェース型であり、Type を利用して元の型のメソッドや要素情報、パッケージのパスなど、型に関するさまざまな情報を参照することができる。※全ての型で Type のメソッドすべてが利用できるというわけではないので、Type の保有メソッドを利用する前には Kind() 関数で型の種別を調査すること。

type MyStruct struct {
	Id   string
	Code int
}

m := new(MyStruct)

rt := reflect.TypeOf(m)
fmt.Println(rt) // *main.MyStruct

Kind() reflect.Kind

Type が保有しているメソッドの一つ。TypeOf などで取得した Type がどのプリミティブ型に該当するかを再起的に検証する。

type MyStruct struct {
	Id   string
	Code int
}

m := new(MyStruct)
rt := reflect.TypeOf(m)
kind := rt.Kind()
fmt.Println(kind) // ptr

m2 := MyStruct{}
rt2 := reflect.TypeOf(m2)
kind2 := rt2.Kind()
fmt.Println(kind2) // struct

type MyMyStruct MyStruct

m3 := MyMyStruct{}
rt3 := reflect.TypeOf(m3)
kind3 := rt3.Kind()
fmt.Println(kind3) // struct

Kind 関数は Kind 型を返却し、 Kind 型については下記のように uint の値と紐づく定数が設定されている。
ValueOf などで得られる reflect.Value に対しても利用でき、返却値についての挙動は同じ。

type Kind uint

const (
	Invalid Kind = iota
	Bool
	Int
	Int8
	Int16
	(略)
	Ptr
	Slice
	String
	Struct
	UnsafePointer
)

TypeOf などで取得したモノのプリミティブな型が目的の型と一致するかどうかなどを検証する際には、Kind 関数で得られた値と上記の定数とで比較を行う。

type MyStruct struct {
	Id   string
	Code int
}

m := new(MyStruct)
rt := reflect.TypeOf(m)
kind := rt.Kind()
fmt.Println(kind == reflect.Array)  // false
fmt.Println(kind == reflect.Struct) // false
fmt.Println(kind == reflect.Ptr)    // true

(Type) Elem() reflect.Type

Type が保有しているメソッドの一つ。TypeOf などで取得した Type の(※)要素型を取得する。
※要素型・・・配列、チャネル、Map、ポインタ、スライスに格納される要素の型

Type の Kind が配列、チャネル、Map、ポインタ、スライス以外の場合は panic を引き起こす。

ValueOf などで得られる reflect.Value に対しても同名の関数が利用可能だが、そちらの返却値は型名ではなく値。

type MyStruct struct {
	Id   string
	Code int
}

m := new(MyStruct)
rt := reflect.TypeOf(m)
fmt.Println(rt.Kind()) // ptr
elem := rt.Elem()
fmt.Println(elem) // main.MyStruct

strSlice := []string{"hogehoge"}
rt2 := reflect.TypeOf(strSlice)
fmt.Println(rt2.Kind()) // slice
elem2 := rt2.Elem()
fmt.Println(elem2) // string

str := "hogehoge"
rt3 := reflect.TypeOf(str)
fmt.Println(rt3.Kind()) // string
elem3 := rt3.Elem()
fmt.Println(elem3) // panic: reflect: Elem of invalid type string

ValueOf(interface{}) reflect.Value

引数に与えられた値について Value 型として初期化された値を取得する。
Value も Kind メソッドを持ち、 ValueOf で返却された値は元の値の Kind を維持する。

type MyStruct struct {
	Id   string
	Code int
}

m := &MyStruct{Id: "id01", Code: 1}
rv := reflect.ValueOf(m)
fmt.Printf("type: %T, value: %v\n", rv, rv) // type: reflect.Value, value: &{id01 1}
fmt.Println(rv.Kind()) // ptr

str := "hoge"
rv2 := reflect.ValueOf(str)
fmt.Printf("type: %T, value: %v\n", rv2, rv2) // type: reflect.Value, value: hoge
fmt.Println(rv2.Kind()) // string

(Value) Indirect(reflect.Value) reflect.Value

ValueOf などで得られた reflect.Value の実体の値を参照する。 Kind が ptr である必要がある。

type MyStruct struct {
	Id   string
	Code int
}

m := &MyStruct{Id: "id01", Code: 1}
rv := reflect.ValueOf(m)
indirect := reflect.Indirect(rv)
fmt.Println(indirect) // {id01 1}

(Value) Set(reflect.Value)

Value のメソッドであり、レシーバに対して引数で与えられた値を代入する。
事前に CanSet 関数で代入可能かどうかを検証すること。

type MyStruct struct {
	Id   string
	Code int
}

target := new(MyStruct)

rvTarget := reflect.ValueOf(target)
indirect := reflect.Indirect(rvTarget)
fmt.Println(target) // &{ 0}

m := MyStruct{Id: "id01", Code: 1}
rvm := reflect.ValueOf(m)
indirect.Set(rvm)

fmt.Println(target) // &{id01 1}

Discussion