はじめに
Go 言語の reflect パッケージは動的に型や値を扱いたい場合に便利です。
このページでは reflect パッケージの詳しい Example をチートシートとしてまとめました。
ご案内
このページの内容を別サイトにまとめなおしました。
目的別に項目を分けたのでやりたいことからコードを逆引きできます。
また、サイト上でコードを即時実行できるように The Go Playground の実行フォームを埋め込んだので、気軽にコードをお試しいただけます。
目次
- 型情報を値として取得する
- 型情報を比較する
- 任意の型の値を引数とする関数を定義する
- リフレクションで変数を更新する
- 構造体のフィールド一覧を取得する
- 構造体のフィールドのタグ情報を取得する
- ゼロ値であるかを調べる
- ゼロ値を取得する
- リフレクションで数値を扱う
- リフレクションで文字列を扱う
- リフレクションで配列を扱う
- リフレクションでスライスを扱う
- リフレクションでマップを扱う
- リフレクションで構造体を扱う
- リフレクションで関数を扱う
- リフレクションでインターフェイスを扱う
- リフレクションで構造体へ再帰的に関数を実行する
チートシート
package main
import (
"fmt"
"reflect"
)
func main() {
{
p("/* 数値 */")
p("// 型の取得")
p(reflect.TypeOf(0)) //=> int
p("// 型の種別の比較")
// 比較対象の定義は https://golang.org/pkg/reflect/#Kind
p(reflect.TypeOf(0).Kind() == reflect.Int) //=> true
p("// 型の比較")
p(reflect.TypeOf(0) == reflect.TypeOf(100)) //=> true
p("// 変数へ reflect 経由で値をセットする")
var i int
// reflect.Value 型に変換
// ポインタでないと値の変更ができないので &i で変数を渡して .Elem() でポインタの値を返している
v := reflect.ValueOf(&i).Elem()
// 値をセットできる
p(v.CanSet()) //=> true
// 整数値のセット
v.SetInt(100)
p(i) //=> 100
// Set で汎用的に reflect.Value をセットできる | Type が異なる場合は panic になる
v.Set(reflect.ValueOf(200))
p(i) //=> 200
p("// reflect から Interface に変換")
if i2, ok := v.Interface().(int); ok {
p(i2) //=> 200
}
}
{
p("/* 配列 */")
p("// 型の取得")
p(reflect.TypeOf([1]int{0})) //=> [1]int
p("// 型の種別の比較")
p(reflect.TypeOf([1]int{0}).Elem()) //=> int
p("// 型の比較")
p(reflect.TypeOf([1]int{0}).Kind() == reflect.Array) //=> true
p(reflect.TypeOf([1]int{0}) == reflect.TypeOf([1]int{100})) //=> true
p("// 配列型の作成")
rt := reflect.ArrayOf(1, reflect.TypeOf(0)) // 引数は要素数と要素の型
p("Type:", rt, "Kind:", rt.Kind()) //=> Type: [1]int Kind: array
p("// 要素数の取得")
p(rt.Len()) //=> 1
p("// 配列の作成と値のセット")
// New で与えた型の値が作成されポインタが返却されるので Elem で値を取得
rv := reflect.New(rt).Elem()
// Index で任意の位置の要素 (reflect.Value) にアクセスできる
p(rv.Index(0))
// SetInt で整数値のセット
rv.Index(0).SetInt(100)
p(rv) //=> [100]
p("// Interface 経由で reflect.Value から [1]int へ変換")
p(rv.Interface().([1]int)) //=> [100]
p("// 定義済みの変数へ値をセット")
ary := [1]int{0}
reflect.ValueOf(&ary).Elem().Index(0).SetInt(500)
p(ary) //=> [500]
}
{
p("/* スライス */")
p("// 型の取得")
p(reflect.TypeOf([]int{})) //=> []int
p(reflect.TypeOf(([]int)(nil))) //=> []int
p("// 型の種別の比較")
p(reflect.TypeOf([1]int{0}).Elem()) //=> int
p("// 型の比較")
p(reflect.TypeOf([]int{}).Kind() == reflect.Slice) //=> true
p(reflect.TypeOf([]int{}) == reflect.TypeOf([]int{1, 2, 3})) //=> true
p("// スライス型の作成")
rt := reflect.SliceOf(reflect.TypeOf(0)) // 引数は要素数と要素の型
p("Type:", rt, "Kind:", rt.Kind()) //=> Type: []int Kind: slice
p("// スライスの作成")
// MakeSlice で要素の型、要素数、容量を指定
rv := reflect.MakeSlice(reflect.TypeOf([]int{}), 1, 1)
// Index で任意の位置の要素 (reflect.Value) にアクセスできる
p(rv.Index(0))
// SetInt で整数値のセット
rv.Index(0).SetInt(100)
p(rv) //=> [100]
// Append での追加もできる
rv = reflect.Append(rv, reflect.ValueOf(200))
p(rv) //=> [100 200]
// AppendSlice でスライス同士の結合もできる
rv = reflect.AppendSlice(rv, reflect.ValueOf([]int{300}))
p(rv) //=> [100 200 300]
p("// Interface 経由で reflect.Value から []int へ変換")
p(rv.Interface().([]int)) //=> [100 200 300]
p("// 定義済みの変数へ値をセット (1)")
s := make([]int, 2, 2)
rv = reflect.ValueOf(&s).Elem()
rv.Index(0).SetInt(500)
rv.Index(1).SetInt(1000)
p(s) //=> [500]
p("// 定義済みの変数へ値をセット (2)")
s = []int{}
rv = reflect.ValueOf(&s).Elem()
rv.Set(reflect.Append(rv, reflect.ValueOf(2000))) // Append は新しい reflect.Value を作成するので Set で元の変数を上書きする
p(s) //=> [2000]
}
{
p("/* マップ */")
p("// 型の取得")
p(reflect.TypeOf(map[string]int{})) //=> map[string]int
p(reflect.TypeOf((map[string]int)(nil))) //=> map[string]int
p("// キーの型の取得")
p(reflect.TypeOf(map[string]int{}).Key()) //=> string
p("// 値の型の取得")
p(reflect.TypeOf(map[string]int{}).Elem()) //=> int
p("// 型の比較")
p(reflect.TypeOf(map[string]int{}).Kind() == reflect.Map) //=> true
p(reflect.TypeOf(map[string]int{}) == reflect.TypeOf(map[string]int{"key1": 1})) //=> true
p("// マップ型の作成")
rt := reflect.MapOf(reflect.TypeOf(""), reflect.TypeOf(0)) // 引数はキーの型と値の型
p("Type:", rt, "Kind:", rt.Kind()) //=> Type: map[string]int Kind: map
p("// マップの作成")
rv := reflect.MakeMap(reflect.TypeOf(map[string]int{}))
// 値のセット
rv.SetMapIndex(reflect.ValueOf("key1"), reflect.ValueOf(100))
// キーのリスト取得
for _, k := range rv.MapKeys() {
p(k)
}
// 値の取得
p(rv.MapIndex(reflect.ValueOf("key1"))) //=> 100
p("// 定義済みの変数へ値をセット")
m := map[string]int{}
rv = reflect.ValueOf(&m).Elem()
rv.SetMapIndex(reflect.ValueOf("key2"), reflect.ValueOf(200))
rv.SetMapIndex(reflect.ValueOf("key3"), reflect.ValueOf(300))
p(m) //=> map[key2:200 key3:300]
}
{
p("/* 構造体 */")
type User struct {
Name string
Age int
}
p("// 型の取得")
p(reflect.TypeOf(User{})) //=> main.User
p("// 型の比較")
p(reflect.TypeOf(User{}).Kind() == reflect.Struct) //=> true
p(reflect.TypeOf(User{}) == reflect.TypeOf(User{Name: "user1", Age: 10})) //=> true
p("// 構造体型の作成")
t := reflect.StructOf([]reflect.StructField{
reflect.StructField{Name: "Name", Type: reflect.TypeOf("")},
reflect.StructField{Name: "Age", Type: reflect.TypeOf(0)},
})
p(t) //=> struct { Name string; Age int }
p("// 構造体の作成")
rv := reflect.New(reflect.TypeOf(User{})).Elem()
p("// フィールドの一覧")
rt := rv.Type()
p(rv, rt)
for i := 0; i < rt.NumField(); i++ {
// フィールドの取得
f := rt.Field(i)
// フィールド名
p(f.Name)
// 型
p(f.Type)
// タグ
p(f.Tag)
}
p("// フィールドの取得")
if f, ok := rt.FieldByName("Name"); ok {
p(f.Name, f.Type) //=> Name string
}
p("// フィールドの更新")
rv.Field(0).SetString("user1")
p(rv.Field(0)) //=> user1
p("// 定義済みの変数へ値をセット")
u := User{}
uv := reflect.ValueOf(&u).Elem()
uv.Field(0).SetString("user2")
uv.Field(1).SetInt(20)
p(u.Name, u.Age) //=> user2 20
}
{
p("/* 関数 */")
fn := func(s string, i int) string {
out := ""
for j := 0; j < i; j++ {
out += s
}
return out
}
p("// 型の取得")
p(reflect.TypeOf(fn)) //=> func(string, int) string
p("// 型の比較")
p(reflect.TypeOf(fn).Kind() == reflect.Func) //=> true
p(reflect.TypeOf(fn) == reflect.TypeOf(func(s string, i int) string { return "" })) //=> true
p("// 引数の一覧")
fnt := reflect.TypeOf(fn)
for i := 0; i < fnt.NumIn(); i++ {
// 引数の型の取得
p(fnt.In(i))
}
p("// 返り値の一覧")
for i := 0; i < fnt.NumOut(); i++ {
// 返り値の型の取得
p(fnt.Out(i))
}
p("// 関数の実行")
fnv := reflect.ValueOf(fn)
out := fnv.Call([]reflect.Value{reflect.ValueOf("hello"), reflect.ValueOf(2)})
if s, ok := out[0].Interface().(string); ok {
p(s) //=> hellohello
}
}
}
func p(a ...interface{}) {
fmt.Println(a...)
}
インターフェイス編(追記)
package main
import (
"reflect"
"fmt"
)
// インターフェイス宣言
type MyInterface1 interface {
GetZero() int
}
// MyInterface1 を実装する構造体の宣言
type MyStruct1 struct{}
func (s MyStruct1) GetZero() int {
return 0
}
// MyInterface2 を実装しない構造体の宣言
type MyStruct2 struct{}
func main() {
p("/* インターフェイス */")
// インターフェイスの初期化. int の 1 を interface{} にキャストする
i1 := interface{}(1)
// インターフェイスの値型の取得
rv := reflect.ValueOf(i1)
// インターフェイスであるかどうかの判定
p(rv.CanInterface()) //=> true
// インターフェイスの値型のタイプ型は元の int 型
p(rv.Type()) //=> int
// 任意のインターフェイス型の取得
// MyInterface1 のタイプ型を取得するために nil を *MyInterface1 にキャストしてタイプ型を取得してから Elem() でポインタを外す
// MyInterface1(nil) とすると MyInterface1 が nil を許容しないためにコンパイルエラーになる
it := reflect.TypeOf((*MyInterface1)(nil)).Elem()
// 任意のタイプ型がインターフェイスの実装であるかどうかの判定
// これだけなら Type Assertion でも同様のことができるが後述するスライスの要素型の判定などをしたい場合などに応用できる
s1 := MyStruct1{}
s2 := MyStruct2{}
p(reflect.TypeOf(s1).Implements(it)) //=> true
p(reflect.TypeOf(s2).Implements(it)) //=> false
// スライスの要素がインターフェイスの実装であるかどうかの判定
list := []MyStruct1{}
p(reflect.TypeOf(list).Elem().Implements(it)) //=> true
}
func p(a ...interface{}) {
fmt.Println(a...)
}
構造体を再帰的に処理する(2019-07-11 追記)
- 構造体のフィールドのタグの記述に従って任意の処理を行う例
- フィールドが構造体・構造体のポインタ・構造体のスライスの場合は再帰的に処理をする
- 例としてフィールドの値がゼロ値ならデフォルト値をセットする
- Playgroun
package main
import (
"fmt"
"reflect"
"strconv"
)
func main() {
// 単純な構造体の場合
type S1 struct {
F1 string `default:"hello"`
F2 int `default:"100"`
F3 float64 `default:"1.5"`
F4 bool `default:"true"`
}
s1 := &S1{}
structFieldRecursiveUpdater(s1, setDefaultValue)
fmt.Printf("%v\n", s1) //=> &{hello 100 1.5 true}
// フィールドに構造体・構造体のポインタ・構造体のスライスをもつ構造体の場合
type S2 struct {
F1 string `default:"hello"`
S1 S1
S1Ptr *S1
S1Slice []S1
}
s2 := &S2{
S1Ptr: &S1{},
S1Slice: []S1{{}, {}},
}
structFieldRecursiveUpdater(s2, setDefaultValue)
fmt.Printf("%v\n", s2) //=> &{hello {hello 100 1.5 true} 0xc4202a2840 [{hello 100 1.5 true} {hello 100 1.5 true}]}
fmt.Printf("%v\n", s2.S1Ptr) //=> &{hello 100 1.5 true}
// フィールドに自身の構造体ポインタを持つ構造体の場合
type S3 struct {
F1 string `default:"hello"`
S3 *S3
}
s3 := &S3{
S3: &S3{
S3: &S3{
S3: &S3{},
},
},
}
structFieldRecursiveUpdater(s3, setDefaultValue)
fmt.Printf("%v\n", s3) //=> &{hello 0xc4200e7f80}
fmt.Printf("%v\n", s3.S3) //=> &{hello 0xc4200e7fa0}
fmt.Printf("%v\n", s3.S3.S3) //=> &{hello 0xc4200e7fc0}
fmt.Printf("%v\n", s3.S3.S3.S3) //=> &{hello <nil>}
}
// 構造体のフィールドを再帰的に読み込んで関数 fn を実行する関数
func structFieldRecursiveUpdater(value interface{}, fn func(reflect.Value, reflect.StructTag)) {
v := reflect.Indirect(reflect.ValueOf(value))
t := v.Type()
switch t.Kind() {
case reflect.Struct:
for i := 0; i < t.NumField(); i++ {
ft := t.Field(i)
fv := v.FieldByName(ft.Name)
if ft.Type.Kind() == reflect.Struct {
// 構造体
structFieldRecursiveUpdater(fv.Addr().Interface(), fn)
} else if ft.Type.Kind() == reflect.Ptr && fv.IsNil() {
// nil ポインタなら処理しない
continue
} else if ft.Type.Kind() == reflect.Ptr && ft.Type.Elem().Kind() == reflect.Struct {
// 構造体のポインタ
structFieldRecursiveUpdater(fv.Interface(), fn)
} else if ft.Type.Kind() == reflect.Slice && ft.Type.Elem().Kind() == reflect.Struct {
// 構造体のスライス
structFieldRecursiveUpdater(fv.Interface(), fn)
} else {
// 任意の型
fn(fv, ft.Tag)
}
}
case reflect.Slice:
for i := 0; i < v.Len(); i++ {
e := v.Index(i)
structFieldRecursiveUpdater(e.Addr().Interface(), fn)
}
}
}
// 値がゼロ値ならタグで指定したデフォルト値をセットする関数
func setDefaultValue(v reflect.Value, tag reflect.StructTag) {
value := tag.Get("default")
if value == "" {
return
}
if v.Interface() != reflect.Zero(v.Type()).Interface() {
return
}
switch v.Kind() {
case reflect.String:
v.SetString(value)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if i, err := strconv.ParseInt(value, 10, 64); err == nil {
v.SetInt(i)
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
if i, err := strconv.ParseUint(value, 10, 64); err == nil {
v.SetUint(i)
}
case reflect.Float32, reflect.Float64:
if f, err := strconv.ParseFloat(value, 64); err == nil {
v.SetFloat(f)
}
case reflect.Bool:
v.SetBool(value == "true")
}
}
宣伝
reflectパッケージについて解説した技術同人誌を作ってみたのでBoothで販売します。興味のある方はぜひご覧ください。
書籍リンク: Go言語reflectパッケージ詳解