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パッケージは現役そうです