一篇文章教會你 Go 語言基礎之反射
前言
Hey,大家好呀,我是碼農,星期八!,我們以前學的呀,都屬於正向定義變量,正向開發!
但是有沒有什麼辦法能反着來呢?根據變量獲取類型等操作。
一起來看看 Go 的反射吧!!!
什麼是反射
反射,嗯...,就是反着的意思唄,就是把東西反過來。
比如這樣的一個很簡單的代碼。
var a int = 3
fmt.Println(a)
我們當然知道a
變量是int
類型,但是反過來想,程序是怎麼知道a
是int
類型呢???
這時候,就需要用到反射了。
示例代碼
v := reflect.TypeOf(a)
fmt.Println(v)
兩次代碼綜合一塊執行結果
第二次的第 2 行代碼,成功的將變量a
還原出了int
類型。
什麼???你爲我有什麼用???,嗯。。。實話實說,用的不是太多,但是必須要會的。
反射 (reflect 包)
在 Go 中,任何變量,都有具體類型和具體值,就像var a int = 3
,具體類型就是int
,具體值就是3
。
所以,變量的具體類型歸屬在reflect.Type
,變量的具體值歸屬在reflect.Value
。
並且 Go 的提供了
-
reflect.TypeOf
獲取具體類型。 -
reflect.ValueOf
獲取具體值。
TypeOf
TypeOf
方法可以獲取變量的具體類型。
有一個這樣的需求,定義一個函數,可以接收任意類型數據,通過反射打印變量類型。
示例代碼
函數
func reflectType(x interface{}) {
v := reflect.TypeOf(x)
fmt.Printf("你傳入的變量類型是:%v\n",v)
}
main
func main() {
var a int = 666
var b float64 = 3.14
var c string = "hello world"
var d [3]int = [3]int{1,2,6}
var e []int = []int{1,2,6,88}
var f map[string]interface{} = map[string]interface{}{
"Name":"張三",
"Age":18,
}
reflectType(a)
reflectType(b)
reflectType(c)
reflectType(d)
reflectType(e)
reflectType(f)
}
執行結果
通過reflect.TypeOf
方法,完美解決上述需求。
TypeOf 的 Name 和 Kind
這個是啥意思呢?? 這個在結構體中比較好體現。
簡答來說就是TypeOf
返回的太籠統了,還有更加細化的類型,通過這倆屬性獲取。
示例代碼
函數
func reflectType(x interface{}) {
v := reflect.TypeOf(x)
fmt.Printf("你傳入的變量類型是:%v | Name:%v | Kind:%v\n", v, v.Name(), v.Kind())
}
結構體
type Student struct {
Name string
Age int
}
main
func main() {
var a int
var b *int
var c []int
var d map[string]interface{}
var e Student
reflectType(a)
reflectType(b)
reflectType(c)
reflectType(d)
reflectType(e)
}
執行結果
總結
經過對比,會發現幾個特殊問題。
如果變量是指針類型,Name
爲空,Kind
是ptr
。
如果變量是引用類型 (切片和 map) 類型,Name
爲空,只有Kind
。
如果變量是結構體,Name
是結構體名,Kind
是struct
。
ValueOf
TypeOf
只能反過來獲取變量的具體類型,但是並不能獲取具體值,這就有點不太厚道了。
所以ValueOf
就來解決這個問題了,但是ValueOf
牛叉的是,它裏面還包括了變量類型。
注:ValueOf
和TypeOf
的Kind
屬性返回內容是一摸一樣的。
需求: 定義一個函數,可以接收任意類型,通過反射得出變量類型和變量值。
函數
func reflectType(x interface{}) {
v := reflect.ValueOf(x)
k := v.Kind()
switch k {
case reflect.Int:
fmt.Printf("我是Int類型,我的值是%v\n",v.Int())
case reflect.Slice:
fmt.Printf("我是切片類型,我的值是%v\n",v.Slice(1,2))
case reflect.Map:
fmt.Printf("我是切片類型,我的值是%v\n",v.MapKeys())
//case :可以繼續case下去
}
}
main
func main() {
var a int = 1
var c []int = []int{1, 5, 7, 19}
var d map[string]interface{} = map[string]interface{}{
"Name": "你好",
"Age": 18,
}
var e Student
reflectType(a)
reflectType(c)
reflectType(d)
reflectType(e)
}
執行結果
通過反射設置值
反射還有一個用途,就是動態的修改變量值,可能你暫時體會不到,但是語法還是要學的。
通過反射設置值,需要用到Elem
方法,並且傳入的必須是指針。
示例代碼
函數
func reflectSetValue(x interface{}) {
v := reflect.ValueOf(x)
//kind也必須是Elem調用
var k = v.Elem().Kind()
switch k {
case reflect.Int:
//反射修改必須通過Elem
v.Elem().SetInt(200)
}
}
main
func main() {
var a int = 10
fmt.Printf("a的值:%v\n", a)
//反射修改值傳入的必須是地址
reflectSetValue(&a)
fmt.Printf("a的值:%v\n", a)
}
執行結果
總結
上述我們學習了 Go 基礎反射的TypeOf
,TypeOf的Name和Kind
,ValueOf
,通過反射設置值
。
其中Kind
在Type
和ValueOf
中都有,通常情況下TypeOf
和ValueOf
一起使用效果更佳!
反射這一塊,可能不是那麼好理解,一定要多多下功夫!堅持!!
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/jR3PEp3YG-2oCDU-ZZjHYQ