実行時に型情報を扱うための3原則
- インタフェース値 → reflect.Value / reflect.Type への変換が可能
- reflect.Value / reflect.Type → インタフェース値 への復元が可能
- reflect.Value で値を書き換えるには「アドレス可能」である必要がある
型システムとreflectの関係
Goでは変数は「静的型」と「実行時型(コンクリート型)」を持つ。
静的型はソースコード上で宣言した型であり、実行時型は実際に格納されている値の型である。
reflectは実行時型を扱うため、空インタフェース(interface{})を経由した値のみが対象となる。
reflect.TypeOf / reflect.ValueOf の基礎
package main
import (
"fmt"
"reflect"
)
func main() {
var price float64 = 9.87
fmt.Println("type :", reflect.TypeOf(price))
fmt.Println("value:", reflect.ValueOf(price))
}
実行結果
type : float64
value: 9.87
reflect.Value から元の値を取り出す
型が事前に分かっている場合
v := reflect.ValueOf(&price)
ptr := v.Interface().(*float64) // *float64 型アサーション
val := reflect.ValueOf(price).Interface().(float64)
型が分からない場合の探索
type Product struct {
ID int
Title string
Stock int
}
func inspect(input interface{}) {
t := reflect.TypeOf(input)
fmt.Println("concrete type:", t)
v := reflect.ValueOf(input)
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
val := v.Field(i).Interface()
fmt.Printf("%s %v = %v\n", f.Name, f.Type, val)
}
for i := 0; i < t.NumMethod(); i++ {
m := t.Method(i)
fmt.Printf("method %s %v\n", m.Name, m.Type)
}
}
func main() {
p := Product{1001, "Keyboard", 42}
inspect(p)
}
値の書き換え
reflect.Value で値を変更するには、対象がアドレス可能(addressable)でなければならない。
つまり、ポインタを介して Elem() で間接参照した reflect.Value でのみ SetXXX 系メソッドが使える。
func main() {
var ratio float64 = 0.5
rv := reflect.ValueOf(&ratio).Elem() // *float64 → float64
if rv.CanSet() {
rv.SetFloat(0.75)
}
fmt.Println(ratio) // 0.75
}
動的メソッド呼び出し
type Counter struct{ n int }
func (c *Counter) Inc() { c.n++ }
func (c Counter) String() string { return fmt.Sprintf("%d", c.n) }
func callByName(obj interface{}, methodName string) {
v := reflect.ValueOf(obj)
m := v.MethodByName(methodName)
if m.IsValid() {
m.Call(nil)
}
}
func main() {
c := &Counter{}
callByName(c, "Inc")
fmt.Println(c) // 1
}
まとめ
- reflect は interface{} を起点に実行時型情報を取得・操作する
- TypeOf / ValueOf でメタデータを取得し、Interface() で元の値に戻せる
- 値の書き換えにはポインタを経由し、Elem() で間接参照した addressable な reflect.Value が必要
- 構造体のフィールドやメソッドも動的に列挙・呼び出し可能
- ジェネリクスがない時代の代替手段として、高度な汎用ライブラリで活用されてきた