GoLang - Reflect 反射
在這幾篇,會以 Go 語言的入門基礎進行逐步說明,本篇針對 reflect 進行說明
在 Go 語言使用 reflect 可以用來取得資料自身的物件結構,動態的調用物件的方法和屬性,機制與 Java 的反射機制類似。
reflect 流程可分成三個步驟
- 將資料本身透過 reflect 轉換為物件結構
- 將物件轉化為對應的值以及類型
- 可修改 reflect 欄位
首先,將資料本身透過 reflect 轉換為物件結構,可以透過 ```reflect.TypeOf````
透過 reflect.ValueOf
CVT2HUGO: 取得 Type 的實例,
CVT2HUGO: 取得值,可直接參考下面的範例:
package main
import (
"fmt"
"reflect"
)
type Member struct {
id int
name string
age int
}
func main() {
member := Member{1, "Adam", 100}
//將資料本身透過 reflect 轉換為物件結構
t := reflect.TypeOf(member) //取得所有元素
v := reflect.ValueOf(member) //獲得值
fmt.Println(t) //output main.Member
fmt.Println(v) //output {1 Adam 100}
}
在前面提到的 TypeOf 回傳實例,是基於 Type Interface 定義進行實作,關於 Type Interface 主要的方法如下:
(可參考 source https://golang.org/src/reflect/type.go)
type Type interface {
Align() int
FieldAlign() int
Method(int) Method
MethodByName(string) (Method, bool)
NumMethod() int
Name() string
PkgPath() string
Size() uintptr
String() string
Kind() Kind
Implements(u Type) bool
AssignableTo(u Type) bool
ConvertibleTo(u Type) bool
Comparable() bool
Bits() int
ChanDir() ChanDir
IsVariadic() bool
Elem() Type
Field(i int) StructField
FieldByIndex(index []int) StructField
FieldByName(name string) (StructField, bool)
FieldByNameFunc(match func(string) bool) (StructField, bool)
In(i int) Type
Key() Type
Len() int
NumField() int
NumIn() int
NumOut() int
Out(i int) Type
common() *rtype
uncommon() *uncommonType
}
因此,透過 TypeOf 獲得中繼資料後,就可使用上面這些方法,
有一些方法需指定的類型才能使用,例如,當我們定義了 Struct ,並且取得他的 reflect 之後,就可以透過 NumField
但是,在如果不是用 struct 呼叫 NumFiled ,則會 panics。
底下範例我們透過 NumField
CVT2HUGO: 取得 struct field 的數量,
CVT2HUGO: 取得 struct field 數量,以及 Field(i) 取得 i 位置的元素,在透過 Name, Type 取得 struct 的欄位以及類型:
package main
import (
"fmt"
"reflect"
)
type Member struct {
id int
name string
age int
}
func main() {
member := Member{1, "Adam", 100}
//將資料本身透過 reflect 轉換為物件結構
t := reflect.TypeOf(member) //取得所有元素
for i := 0; i < t.NumField(); i++ {
file := t.Field(i)
fmt.Println(file.Name, file.Type)
}
}
//output
//id int
//name string
//age int
Kind
在 reflect 的 Kind 方法可以用來取得型別,在之前的章節有提到 comma-ok 來幫助判斷型別,以及透過 switch 的篩選方式。
透過 reflect.TypeOf 的 Kind() 方法,結合 switch 則可以更簡單的做型別判斷,範例如下:
func JudgeType(DataObj interface{}) {
v := reflect.TypeOf(DataObj)
switch v.Kind() {
case reflect.Int:
fmt.Println("int:")
case reflect.String:
fmt.Println("string:")
default:
fmt.Println("no match")
}
}
func main() {
n := 10
JudgeType(n)
a := "hello"
JudgeType(a)
}
Kind 的結構如下:
// A Kind represents the specific kind of type that a Type represents.
// The zero Kind is not a valid 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
Ptr
Slice
String
Struct
UnsafePointer
)
其他更多延伸內容,可參考 The Laws of Reflection 。