STORY
Golang中有一種型態為interface{}, 主要就是像動態語言,
可以賦予它任何型態的值, 如struct, map, slice等...
但當你在使用時, 就需要Reflect來辨認它的型態.
DESCRIPTION
將一個指向Struct的pointer丟到interface中,
此時可以看見他回傳的是一個指向Struct的pointer的Type:
type Struct struct {
Name string
}
func main() {
str := &Struct{
Name: "Struct",
}
var inter interface{} = str
sType := reflect.TypeOf(inter)
fmt.Println(sType) // *main.Struct
}
若今日我有個需求是, 我需要根據interface中的pointer型態,
去建立一個該型態的物件 (非pointer),
那麼可以使用以下方法:
type Struct struct {
Name string
}
func main() {
str := &Struct{
Name: "Struct",
}
// ******** part 1 ********
var inter interface{} = str
sType := reflect.TypeOf(inter)
fmt.Println(sType) // *main.Struct
// ******** part 2 ********
sValue := reflect.New(sType.Elem())
fmt.Println(reflect.TypeOf(sValue.Elem().Interface())) // main.Struct
}
可在part 2發現有個.Elem()的方法, 它有點像在C語言中的取址符&,
但比較特別的是它也可以用在slice身上,
上述就是取Pointer指向的Struct,
並且把它New出來, 而與C相同New出來的type,
一樣會返回一個指向該空間的pointer給你,
所以在最後把它轉為interface的時候,
需要再做一次Elem()的動作.
最後若我們需要針對該型態來建立一個slice該怎麼做呢?
可以參考以下程式碼:
type Struct struct {
Name string
}
func main() {
str := &Struct{
Name: "Struct",
}
// ******** part 1 ********
var inter interface{} = str
sType := reflect.TypeOf(inter)
fmt.Println(sType) // *main.Struct
// ******** part 2 ********
sValue := reflect.New(sType.Elem())
sSliceType := reflect.SliceOf(sType.Elem())
sSliceValue := reflect.MakeSlice(sSliceType, 0, 0)
fmt.Println(sSliceType) // []main.Struct
// ******** part 3 ********
sValue.Elem().Field(0).SetString("simple example")
sSliceValue = reflect.Append(sSliceValue, reflect.ValueOf(sValue.Elem().Interface()))
fmt.Println(sSliceValue) // [{simple example}]
}
在part 2中, 可以利用sliceOf將指定的型態變為slice of type,
以上我就建立了一個slice of struct並且將該type,
使用makeSlice建立出來一個sliceValue,
在part 3中, 將先前建立好的value把值set給他,
記得這裡因為new的時候它是pointer,
所以必須加上Elem()才能附值,
並且把剛剛的值append給part 2中的slice,
同樣需要注意因為slice格式為[]Struct,
所以只能append Struct的interface給他.
最後則是印出的結果.
NOTE:
- 因為reflect是用來在runtime的時候, 判斷interface的值, 所以在使用時須要注意型態的變化.
- 在過程中會用到許多類似pointer的機制, 要是沒有注意, 一不小心就會panic.