マスター・オブ・Reflectパッケージ

Takuya Ueda
Takuya UedaSouzoh, Inc. (affiliated by Mercari, Inc.) - Go Engineer
マスター・オブ
・reflectパッケージ
2013/10/14 Mon
自己紹介
KLab株式会社
エンジニア

上田拓也
主に使用する言語
趣味:Golang, Java,
仕事:PHP, JavaScript

twitter : @tenntenn
今日話すことは...
● Go研 Vol.8でやったことをまとめたもの
○ https://github.
com/goken/goken/blob/master/goken08-reflect.md
アジェンダ
● なぜ、reflectパッケージを使うのか?
● 実際どこで使われているの?
● Value型とType型
● interface{}とreflectパッケージ
● 構造体のリフレクション
● チャネルのリフレクション
● 関数のリフレクション
なぜ、reflectパッケージを使うのか?
動機1: Genericsがない
● こういう事はできない
func foo(in <T>) <T> {//...}
func main() {
n := 100
m := foo(n)
}
動機2:型の情報を取りたい
● 構造体にはタグとしてフィールド情報を埋め込
める → リフレクションで取り出す
type Person struct {
Name string `json:”name”`
Age
}

int

`json:”age”`

→タグ
実際どこで使われているの?
encoding/{json, xml, ...}パッケージ
● JSONなどのシリアライズされたものを構造体に
変換する際に使用される
Go言語の構造体:

JSON:

type Person struct {
Name string `json:”name”`
Aget int `json:”age”`
}

{
“name”: “Gopher”,
“age”: 4
}

タグを使って対応付けがされる!
{text, html}/templateパッケージ
● HTMLなどに任意の型の値を埋め込むために
使われる
Hello, {{.Name}}!!

Execute

Hello, Gopher!!

テンプレート

出力
データの埋込み

Person {
Name: “Gopher”,
Age: 4,
}
Value型とType型
Value型とType型
● Value型
○ 値を表す型
○ 全ての型の値は、reflectパッケージの世界では、Value
型で表される

● Type型
○ 型を表す型
○ 全ての型は、reflectパッケージの世界では、Type型で
表される
Value型の定義
type Value struct {
typ *rtype
// 型
val unsafe.Pointer // 値へのポインタ
flag
// メタ情報
}
Value型の取得とKind
● reflect.ValueOfを使う
v := reflect.Value(100)
● v.Kind()で値の種類がとれる
Invalid Kind = iota
Bool
Int
Int8
Int16
Int32
Int64
Uint
Uint8

Uint16
Uint32
Uint64
Uintptr
Float32
Float64
Complex64
Complex128
Array

Chan
Func
Interface
Map
Ptr
Slice
String
Struct
UnsafePointer
Type型の定義
● インタフェース型
● メソッドやフィールドの情報を取るメソッドがある
● Value型が持っているメソッドに近いものを持っ
ている
Type型の実体
● Type型の実体は*rtype型である
● *rtype型がType型を実装している
type Type interface{
// ….
common() *rtype
uncommon() *uncommonType
}
Type型の取得
● reflect.TypeOf()かValue.Type()で取得できる
t1 := reflect.TypeOf(100)
v := reflect.ValueOf(100)
t2 := v.Type()
● reflect.Newを使えばオブジェクトを作れる
typ := reflect.TypeOf(100)
obj := reflect.New(typ)
interface{}とreflectパッケージ
変数に値を設定する
● ポインタじゃないと設定できない
n := 100
v1 := reflect.ValueOf(n)
fmt.Println(v1.CanSet()) // false=設定
v2 := reflect.ValueOf(&n)
v2.Elem().SetInt(200)
fmt.Println(n) // 200

Elemでポインタの
指してる先を取得
変数に値を設定する
n: 100

&n
ValueOf()

Value
(ポインタ)
Elem()

Set

Value
(Int)
interface{}としてポインタを渡す
● interface{}として、任意の型のポインタを受け取
る

func set(p, v interface{}) {
pv := reflect.ValueOf(p) // ポインタ
vv := reflect.ValueOf(v) // 設定する値
pv.Elem().Set(vv)
}
構造体のリフレクション
フィールドの情報を取得する(Value)
type MyStruct struct {
field1 string
field2 MyStruct2
}
type MyStruct2 struct {
field int
}
ms := MyStruct{

// ms.field1
fmt.Println(v.Field(0))
// ms.field2.field
fmt.Println(v.
FieldByIndex([]int{1,
0}))

"str",
MyStruct2{100},
}
v := reflect.ValueOf(ms)

// ms.field1
fmt.Println(v.
FieldByName("field1"))
フィールドの情報を取得する(Value)
type MyStruct struct {
field1 string
field2 MyStruct2
}
type MyStruct2 struct {
field int
}
ms := MyStruct{
"str",
MyStruct2{100},
}
v := reflect.ValueOf(ms)

f := func(name string) bool {
return name == "field1"
}
fmt.Println(v.FieldByNameFunc
(f))
fmt.Println(v.NumField())
Value型から取得したフィールド
● ポインタ経由でないとSetできない
● privateなフィールドにはSetできない
type Hoge struct {
N int
}
func main() {
h := Hoge{10}
hpv := reflect.ValueOf(&h)
hpv.Elem().FieldByName("N").SetInt(200)
fmt.Println(h)
}
フィールドの情報を取得する(Type)
● タグを取得する
type Hoge struct {
N int `json:"n"`
}

type StructTag string

func main() {
h := Hoge{10}
t := reflect.TypeOf(h)
n, _ := t.FieldByName("N")
fmt.Println(n.Tag.Get("json"))
}
メソッドの情報の取得
● フィールドと同じようにValue型とType型のオブ
ジェクトから情報がとれる
● Value.Call()でメソッドを呼ぶ事ができる
まとめ
何か値

interface{}
reflect.ValueOf()

Value
.Elem().Set()
Value.XXX()

何かの変数の
ポインタ

Value
reflect.ValueOf()

interface{}
残りは、時間があったらやる
チャネルのリフレクション
チャネルのリフレクション
● reflect.MakeChan
○ チャネルを作れる

● Value.Send()
○ チャネルにデータを送る

● Value.Recv()
○ チャネルからデータを受取る

● Value.TrySend(), Value.TryRecv()
○ ブロックしないバージョン

● Value.Len()
○ バッファ内のデータの数

● Value.Cap()
○ バッファの容量
reflect.Select
func Select(cases []SelectCase) (chosen int, recv Value,
recvOK bool)
● 引数
○ cases: caseの配列
● 戻り値
○ chosen: 選んだcaseのインデックス
○ recv: 受取ったチャネル
○ recvOK: 受取れたか
SelectCase
type SelectCase struct {
Dir SelectDir // チャネルの方向
Chan Value
Send Value
}

// Valueにしたチャネル
// 送信する値
SelectDir
type SelectDir int
const (
SelectSend

// case Chan <- Send

SelectRecv

// case <-Chan:

SelectDefault // default
)
関数のリフレクション
Callする
f := func(n int) {
fmt.Println(n)
}
fv := reflect.ValueOf(f)
fmt.Println(fv)
// func (v Value) Call(in []Value) []Value
fv.Call([]reflect.Value{reflect.ValueOf(100)})
関数の情報をType型から取得する
● Type.NumIn() int
○ 引数の数

● Type.In(i int) Type
○ i番目の引数の型を取得する

● Type.NumOut() int
○ 戻り値の数

● Type.Out(i int) Type
○ i番目の戻り値の型を取得する
関数を作る
func(in []Value) []Value
reflect.MakeFunc()

Value
Elem().Set()

Value

&func(...)...
reflect.ValueOf()
関数を作る - 合成関数を作る
func main() {
f := func(x int) int {
return x * x
}
var g func(x int) int
// 2つの関数を受け取り、第3引数の変数に入れる
Compose(f, f, &g)
fmt.Println(g(2)) // 16
}
関数を作る
func Compose(f, g, fptr interface{}) {
fv := reflect.ValueOf(f)
gv := reflect.ValueOf(g)
fgv := func(in []reflect.Value) []reflect.Value {
return gv.Call(fv.Call(in))
}
fn := reflect.ValueOf(fptr).Elem()
v := reflect.MakeFunc(fn.Type(), fgv)
fn.Set(v)
}
まとめ
何か値

interface{}
reflect.ValueOf()

Value
.Elem().Set()
Value.XXX()

何かの変数の
ポインタ

Value
reflect.ValueOf()

interface{}
1 of 41

More Related Content

What's hot(20)

Go入門Go入門
Go入門
Takuya Ueda34.2K views
Go Friday 傑作選Go Friday 傑作選
Go Friday 傑作選
Takuya Ueda2.7K views
T90 きっと怖くないmvvm & mvpvmT90 きっと怖くないmvvm & mvpvm
T90 きっと怖くないmvvm & mvpvm
伸男 伊藤8.2K views
研修成果プレゼン資料研修成果プレゼン資料
研修成果プレゼン資料
Wataru Yamaura24K views
Photon Fusionのはじめの一歩Photon Fusionのはじめの一歩
Photon Fusionのはじめの一歩
聡 大久保940 views
静的型付け言語Python静的型付け言語Python
静的型付け言語Python
kiki utagawa4.6K views
Python と型ヒント (Type Hints)Python と型ヒント (Type Hints)
Python と型ヒント (Type Hints)
Tetsuya Morimoto38.1K views
闇魔術を触ってみた闇魔術を触ってみた
闇魔術を触ってみた
Satoshi Sato6.7K views
C++の黒魔術C++の黒魔術
C++の黒魔術
Daichi OBINATA6.8K views

マスター・オブ・Reflectパッケージ