LoginSignup
1
0

More than 1 year has passed since last update.

Golang reflectパッケージのお勉強

Posted at

TL;DR

reflectパッケージを使うと、構造体のタグの値を取得できる。(実行時に構造体の値から)

はいり

今回はReflectパッケージについて勉強します

パッケージを勉強するときはexamplesを一通り見て回ります。

その際exampleのコードを見ながら自分でもちょっとしたコードを書くので、そのコードを載せます。

Reflectパッケージには8つのexampleがありました。
順番に見ていきましょう

Kind
MakeFunc
StructOf
StructTag
StructTag.Lookup
TypeOf
Value.FieldByIndex
Value.FieldByName

Kind

まずは定義を見ましょう。型の名前っぽいものが羅列されてます。

type Kind uint

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
	Pointer
	Slice
	String
	Struct
	UnsafePointer
)

reflect.Value型にある Kind というメソッドで Kind型の値を取得できます。
Kindというくらいなので、実行時の値の型の種類が取得できます。
注意すべき点は型の名前ではなく種類という点くらいですね。

type (
	User struct {
		ID   uint
		Name string
	}
)

type (
	IFoo interface {
		Hey() string
	}
	FooImpl struct{}
)

func (f FooImpl) Hey() string { return "foo" }

func main() {
	i := 1
	v := reflect.ValueOf(i)
	fmt.Println(v.Kind(), v.Type())

	var foo IFoo = FooImpl{}
	v = reflect.ValueOf(foo)
	fmt.Println(v.Kind(), v.Type())

}

// int int
// struct main.FooImpl

MakeFunc

時代はジェネリクスなので、割愛します。

StructOf

動的に構造体の定義を生成できます。

	typ := reflect.StructOf([]reflect.StructField{
		{
			Name: "Height",
			Type: reflect.TypeOf(float64(0)),
			Tag:  `json:"height"`,
		},
		{
			Name: "Age",
			Type: reflect.TypeOf(int(0)),
			Tag:  `json:"age"`,
		},
	})

	v := reflect.New(typ).Elem()
	fmt.Println(v)
	v.Field(0).SetFloat(0.4)
	v.Field(1).SetInt(2)
	fmt.Println(v)
// {0 0}
// {0.4 2}

このコード例は以下のような構造体定義となります

type Human struct {
  Height float64
  Age    int
}

StructTag

構造体のフィールドにつけるタグのことです。

type S struct {
	F string `species:"gopher" color:"blue"`
}

以下のように実行時の構造体から取得します。
実行時なのが個人的には悲しい点です。
コンパイル時やコンパイル前にタグに楽にアクセスできる方法があればいいなと思います。

	s := S{}
	st := reflect.TypeOf(s)
	field := st.Field(0)
	fmt.Println(field.Tag.Get("color"), field.Tag.Get("species"))

StructTag.Lookup

前述のStructTagのラベルの存在確認です。


	s := S{}
	st := reflect.TypeOf(s)
	for i := 0; i < st.NumField(); i++ {
		field := st.Field(i)
		if alias, ok := field.Tag.Lookup("alias"); ok {
			if alias == "" {
				fmt.Println("(blank)")
			} else {
				fmt.Println(alias)
			}
		} else {
			fmt.Println("(not specified)")
		}
	}

TypeOf

引数の値の型の情報を返す関数です

	t := reflect.TypeOf((*io.Writer)(nil))
	fmt.Println(t)
// *io.Writer

FieldByIndex

プライベートなフィールドにもアクセスできる手段です。

	type user struct {
		firstName string
		lastName  string
	}

	u := user{"suke", "nishi"}
	s := reflect.ValueOf(u).FieldByIndex([]int{0})
	fmt.Println(s)
// suke

FieldByName

プライベートなフィールドにもアクセスできる手段です。
FieldByIndexとほぼ同じなので割愛。

まとめ

Go1.18からジェネリクスが導入されて出番が多少減ってはしまいしたが、
構造体からタグ情報を取得する定番の方法なのでまだまだreflectパッケージは現役そうです

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0