reflection
- 反射(reflection)是程序在運(yùn)行時(shí)通過檢查其定義的變量和值獲得對(duì)應(yīng)的真實(shí)類型芜飘。
在計(jì)算機(jī)科學(xué)領(lǐng)域,反射是指一類應(yīng)用能夠自描述和自控制侠姑。此類應(yīng)用采用某種機(jī)制來實(shí)現(xiàn)自己行為的描述(self-representation)和監(jiān)測(cè)(examination)弱恒,并能根據(jù)自身行為的狀態(tài)和結(jié)果酬蹋,調(diào)整或修改應(yīng)用所描述行為的狀態(tài)和相關(guān)的語義萧吠。
- 反射機(jī)制就是在運(yùn)行時(shí)動(dòng)態(tài)的調(diào)用對(duì)象的方法和屬性
每種編程語言的反射模型都不同左冬,有些語言并不支持反射。支持反射的語言可以在程序編譯期將變量的反射信息纸型,比如字段名稱、類型信息梅忌、結(jié)構(gòu)體信息等整合到可執(zhí)行文件中狰腌,并給程序提供接口訪問反射信息,這樣可在程序運(yùn)行期獲取類型的反射信息牧氮,并有能力改變它們琼腔。
- 反射是指在程序運(yùn)行期對(duì)程序本身進(jìn)行訪問和修改的能力
程序在編譯時(shí)變量會(huì)被轉(zhuǎn)換為內(nèi)存地址,而變量名不會(huì)被編譯器寫入到可執(zhí)行部分踱葛,因此在運(yùn)行程序時(shí)程序是無法獲取自身信息的丹莲。
reflect
Golang提供了一種機(jī)制在運(yùn)行時(shí)更新和檢查變量的值、調(diào)用變量的方法和變量支持的內(nèi)在操作尸诽,但在編譯時(shí)并不知道這些變量的具體類型甥材,這種機(jī)制稱為反射。
反射可將類型本身作為第一類型的值來處理
- Golang程序在運(yùn)行期可使用
reflect
包訪問程序的反射信息
Golang提供了reflect
包用來實(shí)現(xiàn)反射性含,通過refelct
包能夠完成對(duì)一個(gè)interface{}
空接口變量的具體類型和值進(jìn)行獲取洲赵。
Golang程序的反射系統(tǒng)無法獲取到一個(gè)可執(zhí)行文件空間中或一個(gè)包中的所有類型信息,需配合標(biāo)準(zhǔn)庫中對(duì)應(yīng)的此法商蕴、語法解析器和抽象語法樹(AST)對(duì)源碼進(jìn)行掃描后獲取叠萍。
interface
- Golang關(guān)于類型設(shè)計(jì)的原則
變量包括(value, type)
兩部分,type
包括static type
和concrete type
绪商,簡(jiǎn)單來說static type
是在編碼時(shí)可見的類型苛谷,concrete type
是runtime
系統(tǒng)可見的類型。
類型斷言能夠成功取決于變量的concrete type
而非static type
格郁,比如一個(gè)變量的concrete type
如果實(shí)現(xiàn)了write
方法則可被類型斷言為writer
腹殿。
- 反射是建立在類型之上的
Golang指定類型的變量是static
的,因此在創(chuàng)建變量時(shí)就已經(jīng)確定理张。反射主要與Golang的interface
類型相關(guān)赫蛇,因?yàn)?code>interface的類型是concrete
的,只有interface
類型才有反射一說雾叭。
-
interface
與pair
的存在是Golang中實(shí)現(xiàn)反射的前提
在Golang的實(shí)現(xiàn)中每個(gè)interface
變量都擁有一對(duì)pair(value, type)
悟耘,pair
中記錄了實(shí)際變量的值和類型(value, type)
。
value
是實(shí)際變量的值
type
是實(shí)際變量的類型
一個(gè)interface{}
類型的變量包含兩個(gè)指針:
一個(gè)指針下指向?qū)嶋H的值织狐,即對(duì)應(yīng)的value
暂幼。
一個(gè)指針指向值的類型筏勒,對(duì)應(yīng)concrete type
。
例如:創(chuàng)建類型為*os.File
的變量
tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
將變量賦值給一個(gè)接口變量
var r io.Reader
r = tty
賦值后接口變量的pair
中記錄的信息為(tty, *os.File)
旺嬉,這個(gè)pair
在接口變量連續(xù)賦值過程中是不會(huì)改變的管行。
比如將接口變量賦值給另一個(gè)接口變量
var w io.Writer
w = r.(io.Writer)
此時(shí)接口變量w的pair和r的pair相同,都是(tty, *os.File)
邪媳。即使w是空接口類型捐顷,pair
也是不變的。
方法
Golang中反射是由reflect
包支持的雨效,reflect
包提供了兩個(gè)重要的類型Type
和Value
迅涮。任意接口值在反射中都可以理解為由reflect.Type
和reflect.Value
兩部分組成。
反射是用來檢測(cè)存儲(chǔ)在接口變量?jī)?nèi)部(值 value
, 類型 concrete type
)pair
對(duì)的一種機(jī)制徽龟,reflect
包提供了reflect.TypeOf
和reflect.ValueOf
兩個(gè)函數(shù)來獲取任意對(duì)象的Value
和Type
叮姑。
-
reflect.ValueOf
用于獲取輸入?yún)?shù)接口中的數(shù)據(jù)的值,若接口為空則返回0据悔。
func reflect.ValueOf(i interface{}) reflect.Value
-
reflect.TypeOf
用于動(dòng)態(tài)獲取輸入?yún)?shù)接口中值的類型传透,若接口為空則返回nil
。
func reflect.TypeOf(i interface{}) reflect.Type
reflect.TypeOf()
用于獲取pair(value, type)
中的type
极颓,reflect.ValueOf()
獲取獲取pair(value, type)
中的value
朱盐。
例如:將float64
類型的變量作為原值傳遞給reflect.TypeOf(i interface{})
獲取其類型信息
var num float64 = 3.14156
t.Log(reflect.ValueOf(num)) //3.14156
t.Log(reflect.TypeOf(num)) //float64
例如:
type Response struct {
Code int
Message string
}
func TestReflect(t *testing.T) {
var response Response
t.Log(reflect.ValueOf(response)) //{0 }
t.Log(reflect.TypeOf(response)) //test.Response
}
定律
反射三大定律
- 反射可以將【接口類型變量】轉(zhuǎn)換為【反射類型對(duì)象】
- 反射類型對(duì)象指的是
reflect.Type
和reflect.Value
- 反射提供了一種允許程序在運(yùn)行時(shí)檢查接口變量?jī)?nèi)部存儲(chǔ)的
pair(value, type)
對(duì)的機(jī)制 -
reflect
包中的Value
和Type
類型時(shí)訪問接口內(nèi)部的數(shù)據(jù)成為可能 -
reflect
包為Value
和Type
類型分別提供了reflect.ValueOf()
和reflect.TypeOf()
方法來進(jìn)行讀取
- 反射可以將【反射類型對(duì)象】轉(zhuǎn)換為【接口類型變量】
- 和物理學(xué)中的反射類似,Golang中的反射也能夠創(chuàng)造自己反面類型的對(duì)象讼昆。
- 若要修改【反射類型對(duì)象】則其值必須可寫
- 可通過
value.CanSet()
方法來檢查一個(gè)reflect.Value
類型的變量是否具有“可寫性” - 可寫性類似于尋址能力托享,但更為嚴(yán)格。它是反射類型變量的一種屬性浸赫,會(huì)賦予該變量修改底層存儲(chǔ)數(shù)據(jù)的能力闰围。
- 可寫性最終是由一個(gè)反射對(duì)象是否存儲(chǔ)原始值而決定的
結(jié)構(gòu)體
- 使用反射獲取結(jié)構(gòu)體中的字段信息
通過reflect.TypeOf()
從結(jié)構(gòu)體中獲取反射類型對(duì)象reflect.Type
,可通過reflect.Type
獲取結(jié)構(gòu)體成員信息既峡。
結(jié)構(gòu)體成員訪問方法列表羡榴,與成員獲取相關(guān)的reflect.Type
的方法。
方法 | 返回值 | 描述 |
---|---|---|
type.Field(index int) | StructField | 根據(jù)索引獲取對(duì)應(yīng)結(jié)構(gòu)體字段 |
type.NumField() | int | 獲取結(jié)構(gòu)體成員字段數(shù)量 |
type.FieldByName(name string) | (StructField, bool) | 根據(jù)指定字符串獲取對(duì)應(yīng)的結(jié)構(gòu)體字段 |
type.FieldByIndex(index []int) | StructField | 多層成員訪問运敢,根據(jù)[]int提供的結(jié)構(gòu)體字段索引獲取字段信息校仑。 |
type.FieldByNameFunc(match func(string) bool) | (StructField, bool) | 根據(jù)匹配函數(shù)獲取字段 |
例如:通過反射值對(duì)象reflect.Value
獲取結(jié)構(gòu)體成員信息
package main
import (
"fmt"
"reflect"
)
type User struct {
Id int
Name string
}
func main() {
user := &User{Id: 1, Name: "root"}
val := reflect.ValueOf(user)
fmt.Printf("%v\n", val) //&{1 root}
elem := val.Elem()
fmt.Printf("%v\n", elem) //{1 root}
elemType := elem.Type()
fmt.Printf("%v\n", elemType) //main.User
numField := elem.NumField()
fmt.Printf("%v\n", numField) //2
for i := 0; i < numField; i++ {
fieldName := elemType.Field(i).Name
field := elem.Field(i)
fmt.Printf("%v\n", field)
fieldType := field.Type()
fieldValue := field.Interface()
fmt.Printf("%d: %s %s = %v\n", i, fieldName, fieldType, fieldValue)
}
}
&{1 root}
{1 root}
main.User
2
1
0: Id int = 1
root
1: Name string = root
reflect.StructField
- 結(jié)構(gòu)體字段類型
reflect.Type
的Field()
方法返回StructField
結(jié)構(gòu),描述了結(jié)構(gòu)體的成員信息传惠,可獲取成員與結(jié)構(gòu)體的關(guān)系迄沫,比如偏移、索引卦方、是否為匿名字段羊瘩、結(jié)構(gòu)體標(biāo)簽等。
type StructField struct{
Name string //字段名
PkgPath string //字段路徑
Type Type //字段反射類型對(duì)象
Tag StructTag//字段的結(jié)構(gòu)體標(biāo)簽
Offset uintptr //字段在結(jié)構(gòu)體中的相對(duì)偏移
Index []int //Type.FieldByIndex返回的索引值
Anonymous bool//是否為匿名字段
}
字段 | 類型 | 描述 |
---|---|---|
Name | string | 字段名稱 |
PkgPath | string | 字段在結(jié)構(gòu)體中的路徑 |
Type | Type | 字段本身的反射類型對(duì)象,類型為reflect.Type尘吗。 |
Tag | StructTag | 結(jié)構(gòu)體標(biāo)簽逝她,為結(jié)構(gòu)體字段標(biāo)簽的額外信息,可單獨(dú)提取睬捶。 |
Index | FieldByIndex | 索引順序 |
Anonymous | bool | 字段是否為匿名字段 |
- 獲取結(jié)構(gòu)體成員反射信息
例如:實(shí)例化結(jié)構(gòu)體后遍歷成員黔宛,通過reflect.Type
的FieldByName()
獲取字段信息。
//聲明結(jié)構(gòu)體
type User struct {
Id int
Name string `json:"name" id:"100"`
}
//創(chuàng)建結(jié)構(gòu)體實(shí)例
ins := User{Id: 1, Name: "root"}
//獲取結(jié)構(gòu)體實(shí)例的反射類型對(duì)象
typ := reflect.TypeOf(ins)
//遍歷結(jié)構(gòu)體成員
for i := 0; i < typ.NumField(); i++ {
//獲取成員
field := typ.Field(i)
//獲取成員屬性
name := field.Name
tag := field.Tag
fmt.Printf("name = %v, tag = %v\n", name, tag)
}
//聲明結(jié)構(gòu)體
type User struct {
Id int
Name string `json:"name" id:"100"`
}
//創(chuàng)建結(jié)構(gòu)體實(shí)例
ins := User{Id: 1, Name: "root"}
//獲取結(jié)構(gòu)體實(shí)例的反射類型對(duì)象
typ := reflect.TypeOf(ins)
//通過字段名獲取信息
field, ok := typ.FieldByName("Name")
if ok {
tag := field.Tag
tagName := tag.Get("json")
tagId := tag.Get("id")
fmt.Printf("name = %v, id = %v\n", tagName, tagId) //name = name, id = 100
}
reflect.StructTag
- 結(jié)構(gòu)體標(biāo)簽
通過reflect.Type
獲取結(jié)構(gòu)體成員reflect.StructField
結(jié)構(gòu)中的Tag
即結(jié)構(gòu)體標(biāo)簽StructTag
擒贸。
結(jié)構(gòu)體標(biāo)簽是對(duì)結(jié)構(gòu)體字段的額外信息標(biāo)簽
JSON臀晃、BSON等格式進(jìn)行序列化,ORM關(guān)系對(duì)象映射都會(huì)使用到結(jié)構(gòu)體標(biāo)簽酗宋,使用標(biāo)簽設(shè)定字段在處理時(shí)應(yīng)具備的特殊屬性和可能發(fā)生的行為积仗。這些信息都是靜態(tài)的,無需實(shí)例化結(jié)構(gòu)體可通過反射獲取到蜕猫。
結(jié)構(gòu)體標(biāo)簽書寫格式:key1:"value1" key2:"value2"
結(jié)構(gòu)體標(biāo)簽由一個(gè)或多個(gè)鍵值對(duì)組成,鍵與值使用冒號(hào)分割哎迄,值使用雙引號(hào)包裹回右,鍵值對(duì)之間使用空格分割。
從結(jié)構(gòu)體標(biāo)簽中獲取值
StructTag
擁有方法可以對(duì)Tag
信息進(jìn)行解析和提取
- Get方法根據(jù)Tag中的鍵獲取對(duì)應(yīng)的值
func (tag StructTag) Get(key string) string
- Lookup方法根據(jù)Tag中的鍵查詢值是否存在
func (tag StructTag) Lookup(key string) (value string, ok bool)
例如:
//聲明結(jié)構(gòu)體
type User struct {
Id int `json:"id" bson:"id"`
Name string `json:"name" bson:"name" orm:"size(32)"`
}
//創(chuàng)建結(jié)構(gòu)體實(shí)例
ins := User{Id: 1, Name: "root"}
//獲取結(jié)構(gòu)體實(shí)例的反射類型對(duì)象
typ := reflect.TypeOf(ins)
//通過字段名獲取信息
field, ok := typ.FieldByName("Name")
if ok {
tag := field.Tag
val, ok := tag.Lookup("bson")
if ok {
fmt.Printf("bson = %v\n", val) //bson = name
}
val = tag.Get("json")
fmt.Printf("json = %v\n", val) //json = name
}
反射值對(duì)象
reflect.ValueOf
func reflect.ValueOf(rawValue interface{}) reflect.Value
例如:
val := reflect.ValueOf(nil)
fmt.Printf("%v\n", val) //<invalid reflect.Value>
例如:動(dòng)態(tài)調(diào)用無參數(shù)的結(jié)構(gòu)體方法
package test
import (
"fmt"
"reflect"
"testing"
)
type User struct {
Id int
Name string
}
func (u *User) Do() {
fmt.Printf("User Do...\n")
}
func TestReflect(t *testing.T) {
ins := &User{Id: 1, Name: "root"}
val := reflect.ValueOf(ins)
fname := "Do"
val = val.MethodByName(fname)
val.Call(nil)
}
例如:動(dòng)態(tài)調(diào)用結(jié)構(gòu)體帶參數(shù)的方法
package test
import (
"fmt"
"reflect"
"testing"
)
type User struct {
Id int
Name string
}
func (u *User) Do(id int, msg string) {
fmt.Printf("%v %v\n", id, msg)
}
func TestReflect(t *testing.T) {
ins := &User{Id: 1, Name: "root"}
val := reflect.ValueOf(ins)
fname := "Do"
val = val.MethodByName(fname)
id := reflect.ValueOf(1)
msg := reflect.ValueOf("hello")
in := []reflect.Value{id, msg}
val.Call(in)
}
reflect.Value
-
reflect.ValueOf(rawValue interface)
原值會(huì)轉(zhuǎn)換為類型為reflect.Value
的反射值對(duì)象漱挚。
type Value struct {
typ *rtype
ptr unsafe.Pointer
flag
}
-
reflect.Value
反射值對(duì)象可與原值通過包裝和值獲取相互轉(zhuǎn)化
通過reflect.Value
重新獲取原始值
方法 | 返回值 | 描述 |
---|---|---|
Interface() | interface{} | 通過類型斷言轉(zhuǎn)換為指定類型 |
Int() | int64 | 將值以int類型返回 |
Uint() | uint64 | 將值以u(píng)int類型返回 |
Float() | float64 | 將值以雙精度類型返回 |
Bool() | bool | 將值以bool類型返回 |
Bytes() | []byte | 將值以字節(jié)數(shù)組類型返回 |
String() | string | 將值以字符串類型返回 |
例如:
var i int = 1024
val := reflect.ValueOf(i)
var j int = val.Interface().(int)
fmt.Printf("j = %v\n", j)
var k int = int(val.Int())
fmt.Printf("k = %v\n", k)
- 反射值對(duì)象
reflect.Value
對(duì)零值和空進(jìn)行有效性判斷
方法 | 返回值 | 描述 |
---|---|---|
value.IsNil () | bool | 判斷返回值是否為nil
|
value.IsValid() | bool | 判斷返回值是否合法 |
func (v Value) IsNil() bool
若值類型不是通道翔烁、函數(shù)、接口旨涝、映射蹬屹、指針、切片時(shí)會(huì)發(fā)生panic
白华,類似語言層面的v == nil
操作慨默。
func (v Value) IsValid() bool
若值本身非法則返回false
例如:判斷空指針
var ptr *int
isNil := reflect.ValueOf(ptr).IsNil()
fmt.Printf("%v\n", isNil) //true
例如:判斷nil
是否非法
isValid := reflect.ValueOf(nil).IsValid()
fmt.Printf("%v\n", isValid) //false
- 通過反射修改變量的值
使用reflect.Value
對(duì)包裝的值進(jìn)行修改時(shí),需遵循一定規(guī)則弧腥,若沒有按照規(guī)則進(jìn)行代碼設(shè)計(jì)和編寫厦取,則無法修改對(duì)象的值或運(yùn)行時(shí)發(fā)生宕機(jī)。
反射值對(duì)象的判定及獲取元素的方法
方法 | 返回值 | 描述 |
---|---|---|
value.Elem() | reflect.Value | 獲取值指向的元素值管搪,類似* 操作虾攻,當(dāng)值類型不是指針或接口時(shí)會(huì)發(fā)生宕機(jī),空指針返回nil 的Value 更鲁。 |
value.Addr() | reflect.Value | 對(duì)可尋址的值返回其地址霎箍,類似& 操作,但值不可尋址時(shí)發(fā)生宕機(jī)澡为。 |
value.CanAddr() | bool | 值是否可尋址 |
value.CanSet() | bool | 值是否可被修改漂坏,要求值可尋址且是可導(dǎo)出的字段。 |
反射類型對(duì)象
reflect.Type
根據(jù)傳入的結(jié)構(gòu)體生成SQL語句
func BuildInsertSQL(obj interface{}) string {
val := reflect.ValueOf(obj)
typ := reflect.TypeOf(obj)
//判斷參數(shù)是否為結(jié)構(gòu)體
if val.Kind() != reflect.Struct {
panic("unsupported type")
}
//獲取結(jié)構(gòu)體名作為數(shù)據(jù)表名
typename := typ.Name()
//遍歷結(jié)構(gòu)體字段
var cols []string
var vals []string
for i := 0; i < val.NumField(); i++ {
//獲取字段名稱
col := typ.Field(i).Name
cols = append(cols, fmt.Sprintf("`%s`", col))
//獲取字段值
var v string
field := val.Field(i)
switch field.Kind() {
case reflect.String:
v = field.String()
case reflect.Int:
v = strconv.FormatInt(field.Int(), 10)
default:
}
vals = append(vals, v)
}
//轉(zhuǎn)換為字符串
colstr := strings.Join(cols, ",")
valstr := strings.Join(vals, ",")
//拼接SQL
query := fmt.Sprintf("INSERT INTO `%s`(%s) VALUES(%s)", typename, colstr, valstr)
return query
}
type User struct {
id int
name string
}
user := User{id: 1, name: "admin"}
sql := BuildInsertSQL(user)
fmt.Println(sql)
INSERT INTO `User`(`id`,`name`) VALUES(1,admin)
reflect.Elem
- 指針與指針指向的元素
func (v reflect.Value) Elem() reflect.Value
Golang對(duì)指針獲取反射對(duì)象時(shí),通過reflect.Elem()
方法獲取指針指向的元素類型樊拓,這個(gè)獲取過程被稱為取元素纠亚,等效于對(duì)指針類型變量做了一個(gè)*
操作。
Golang反射中對(duì)指針變量的種類都是Ptr
package test
import (
"fmt"
"reflect"
"testing"
)
//聲明空結(jié)構(gòu)體
type User struct {
}
func TestReflect(t *testing.T) {
//創(chuàng)建指針變量
ins := &User{}
//獲取指針變量的反射類型信息
typ := reflect.TypeOf(ins)
//獲取指針變量的類型名稱
name := typ.Name()
//獲取指針變量的種類
kind := typ.Kind()
fmt.Printf("name = %v, kind = %v\n", name, kind) //name = , kind = ptr
//獲取指針變量的元素類型
elem := typ.Elem()
//獲取指針類型元素的名稱
name = elem.Name()
//獲取指針類型元素的種類
kind = elem.Kind()
fmt.Printf("name = %v, kind = %v\n", name, kind) //name = User, kind = struct
}
value.Elem()
方法會(huì)返回反射值對(duì)象所包含的值或指針指向的值筋夏,若反射值對(duì)象的種類Kind
不是接口或指針蒂胞,則會(huì)發(fā)生panic
。若反射值為0則返回零值条篷。
ptr := (*int)(nil)
val := reflect.ValueOf(ptr)
fmt.Printf("%v\n", val) //<nil>
elem := val.Elem()
fmt.Printf("%v\n", elem) //<invalid reflect.Value>
isValid := elem.IsValid()
fmt.Printf("%v\n", isValid) //false
reflect.Indirect
func reflect.Indirect(v reflect.Value) reflect.Value
Golang的reflect.Indirect()
函數(shù)用于獲取v
指向的值骗随,若v
為nil
指針則返回零值,若v
不為指針返回為v
赴叹。
與reflect.Elem()
方法不同的是鸿染,Elem()
方法當(dāng)調(diào)用者的Kind
種類不是Interface
接口或Ptr
指針類型時(shí)衷咽,會(huì)報(bào)panic
庆寺。如果調(diào)用者為0,則返回零值津坑。Indirect()
的參數(shù)如果為nil
指針時(shí)會(huì)返回零值绽媒。
當(dāng)調(diào)用者或參數(shù)reflect.Value
為指針時(shí)蚕冬,value.Elem()
等效于reflect.Indirect(value)
。若不是指針則并非等效是辕。
reflect.Indirect()
用于需要接受待定類型或指向該類型的指針囤热。
reflect.Kind
編程中使用最多的是類型(Type),反射中當(dāng)需要區(qū)別一個(gè)大品種的類型時(shí)使用種類(Kind)获三。
例如:需要統(tǒng)一判斷類型中的指針時(shí)旁蔼,使用種類(Kind)較為方便。
Golang中類型(Type)是指系統(tǒng)原生的數(shù)據(jù)類型疙教,比如int
棺聊、string
、bool
松逊、float32
等躺屁,以及使用type
關(guān)鍵字定義的類型,這些類型的名稱就是其類型本身的名稱经宏。
種類(Kind)指的是對(duì)象歸屬的品種
reflect
包中定義的種類Kind
type Kind uint
種類常量 | 描述 |
---|---|
Invalid Kind | 非法類型 |
Bool | 布爾型 |
Int | 有符號(hào)整型 |
Int8 | 有符號(hào)8位整型 |
Int16 | 有符號(hào)16位整型 |
Int32 | 有符號(hào)32位整型 |
Uint | 無符號(hào)整型 |
Uint8 | 無符號(hào)8位整型 |
Uint16 | 無符號(hào)16位整型 |
Uint32 | 無符號(hào)32位整型 |
Uint64 | 無符號(hào)64位整型 |
Uintptr | 指針 |
Float32 | 單精度浮點(diǎn)數(shù) |
Float64 | 雙精度浮點(diǎn)數(shù) |
Complex64 | 64位復(fù)數(shù)類型 |
Complet128 | 128位復(fù)數(shù)類型 |
Array | 數(shù)組 |
Chan | 通道 |
Func | 函數(shù) |
Interface | 接口 |
Map | 映射 |
Ptr | 指針 |
Slice | 切片 |
String | 字符串 |
Struct | 結(jié)構(gòu)體 |
UnsafePointer | 底層指針 |
Map犀暑、Slice、Chan屬于引用類型烁兰,使用起來類似指針耐亏,但在種類中仍屬于獨(dú)立的種類,而不屬于Ptr沪斟。
例如:
type User struct{}
User結(jié)構(gòu)體屬于Struct種類广辰,*User屬于Ptr種類暇矫。
從類型對(duì)象reflect.Type
中獲取類型名稱Name
和種類Kind
- 類型名稱對(duì)應(yīng)的反射獲取方法是
reflect.Type
中的Name()
方法,返回表示類型名稱的字符串择吊。 - 類型歸屬的種類
Kind
使用的是reflect.Type
中的Kind()
方法李根,返回reflect.Kind
類型的常量。
例如:從常量中獲取類型信息
package test
import (
"fmt"
"reflect"
"testing"
)
//定義枚舉類型
type Enum int
//定義常量
const (
Zero Enum = 0
)
func TestReflect(t *testing.T) {
typ := reflect.TypeOf(Zero)
name := typ.Name() //Enum
kind := typ.Kind() //int
fmt.Printf("Name = %v, Kind = %v\n", name, kind) //Name = Enum, Kind = int
}
例如:從結(jié)構(gòu)體中獲取類型信息
package test
import (
"fmt"
"reflect"
"testing"
)
type Response struct {
Code int
Message string
}
func TestReflect(t *testing.T) {
//結(jié)構(gòu)體實(shí)例
response := Response{}
//獲取結(jié)構(gòu)體實(shí)例的反射類型對(duì)象
typ := reflect.TypeOf(response)
//獲取反射類型對(duì)象的名稱和種類
name := typ.Name() //Response
kind := typ.Kind() //struct
fmt.Printf("Name = %v, Kind = %v\n", name, kind) //Name = Response, Kind = struct
}
type.Implements
- 判斷實(shí)例是否已實(shí)現(xiàn)接口
創(chuàng)建接口
type IT interface {
test()
}
創(chuàng)建結(jié)構(gòu)體并實(shí)現(xiàn)接口方法
type T struct {
}
func (t *T) test() {
fmt.Printf("T struct test run...\n")
}
判斷結(jié)構(gòu)體是否實(shí)現(xiàn)接口
t1 := reflect.TypeOf(&T{})
t2 := reflect.TypeOf((*IT)(nil)).Elem()
ok := t1.Implements(t2)
fmt.Printf("%v\n", ok)
reflect.Addr
func (v reflect.Value) reflect.Addr() reflect.Value
Golang中的reflect.Addr()
函數(shù)用于獲取表示v
地址的指針值
reflect.New
func reflect.New(typ reflect.Type) reflect.Value
reflect.SliceOf
func reflect.SliceOf(t reflect.Type) reflect.Type
- reflect.SliceOf() 函數(shù)用于獲取元素類型為
t
的切片類型几睛,如果t
表示int
房轿,SliceOf(t)
表示[]int
。
value.Call
- 反射中函數(shù)和方法的調(diào)用
func (v reflect.Value) Call(in []reflect.Value) []reflect.Value
- 反射中調(diào)用函數(shù)
例如:Golang中函數(shù)類型可以像普通類型等類型變量作為值來使用
func test(){
fmt.Println("fn running....")
}
func main(){
fn := test
fn()//fn running....
}
既然函數(shù)可以像普通類型變量一樣所森,那么在反射機(jī)制中和不同的變量是一樣的囱持。
反射中函數(shù)和方法的類型(Type)都是reflect.Func
,若需調(diào)用函數(shù)可通過Value
的Call()
方法實(shí)現(xiàn)焕济。
func test(){
fmt.Println("fn running....")
}
func main(){
fn := test
rv := reflect.ValueOf(fn)
if rv.Kind() != reflect.Func{
fmt.Println("INVIDAD TYPE")
}
rv.Call(nil)
}
reflect.Value
的Call()
方法的參數(shù)是一個(gè)[] reflect.Value
切片纷妆,對(duì)應(yīng)的反射函數(shù)類型的類型,返回值也是一個(gè)reflect.Value
的[] reflect.Value
切片晴弃,同樣對(duì)應(yīng)反射函數(shù)類型的返回值掩幢。
func test(i int) string{
fmt.Printf("fn running.... %d\n", i)
return strconv.Itoa(i)
}
func main(){
fn := test
//判斷類型
rv := reflect.ValueOf(fn)
if rv.Kind() != reflect.Func{
fmt.Println("INVALID TYPE")
}
//設(shè)置參數(shù)
args := make([]reflect.Value, 1)
args[0] = reflect.ValueOf(100)
//調(diào)用函數(shù)
vs := rv.Call(args)
fmt.Printf("%v\n", vs)//[100]
fmt.Printf("%v\n", vs[0])//100
rs := vs[0].Interface().(string)
fmt.Printf("%v\n", rs)//100
}
- 反射中調(diào)用方法
反射中方法的調(diào)用,函數(shù)和方法本質(zhì)上是相同的上鞠,只不過方法與一個(gè)對(duì)象進(jìn)行了綁定粒蜈,方法是對(duì)象的一種行為,這種行為是對(duì)于這個(gè)對(duì)象的一系列操作旗国。
type Users struct{
id int
name string
}
func (u *Users) SetId(id int){
u.id = id
}
func (u *Users) SetName(name string){
u.name = name
}
func (u *Users) String() string{
return fmt.Sprintf("%p", u) + " name:"+u.name + " id:" + strconv.Itoa(u.id)
}
func main(){
u := &Users{1, "root"}
rv := reflect.ValueOf(&u).Elem()
vs := rv.MethodByName("String").Call(nil)
fmt.Printf("%v\n", vs[0])//0xc000004480 name:root id:1
args := make([]reflect.Value, 1)
args[0] = reflect.ValueOf(100)
vs = rv.MethodByName("SetId").Call(args)
fmt.Printf("%v\n", vs)//[]
args[0] = reflect.ValueOf("admin")
vs = rv.MethodByName("SetName").Call(args)
fmt.Printf("%v\n", vs)//[]
vs = rv.MethodByName("String").Call(nil)
fmt.Printf("%v\n", vs[0])//0xc000004480 name:admin id:100
}