Go進(jìn)階:反射3定律

各位學(xué)習(xí)Go語言的朋友兔毒,周末好咖楣,這次跟大家聊一聊Go語言的一個(gè)高級(jí)話題:反射。

這篇文章是從我過去的學(xué)習(xí)筆記修改來的处渣,內(nèi)容主要來自Go Blog的一篇文章《The law of reflection》伶贰。

這篇文章主要介紹反射和接口的關(guān)系,解釋內(nèi)在的關(guān)系和原理罐栈。

反射來自元編程黍衙,指通過類型檢查變量本身數(shù)據(jù)結(jié)構(gòu)的方式,只有部分編程語言支持反射荠诬。

類型

反射構(gòu)建在類型系統(tǒng)之上琅翻,Go是靜態(tài)類型語言,每一個(gè)變量都有靜態(tài)類型柑贞,在編譯時(shí)就確定下來了方椎。

比如:

type MyInt int

var i int
var j MyInt

i和j的底層類型都是int,但i的靜態(tài)類型是int钧嘶,j的靜態(tài)類型是MyInt棠众,這兩個(gè)是不同類型,是不能直接賦值的有决,需要類型強(qiáng)制轉(zhuǎn)換闸拿。

接口類型比較特殊轿亮,接口類型的變量被多種對(duì)象類型賦值,看起來像動(dòng)態(tài)語言的特性胸墙,但變量類型始終是接口類型我注,Go是靜態(tài)的。舉例:

var r io.Reader
r = os.Stdin
r = bufio.NewReader(r)
r = new(bytes.Buffer)
// and so on

雖然r被3種類型的變量賦值迟隅,但r的類型始終是io.Reader但骨。

最特別:空接口interface{}的變量可以被任何類型的值賦值,但類型一直都是interface{}智袭。

接口的表示

Russ Cox(Go語言創(chuàng)始人)在他的博客詳細(xì)介紹了Go語言接口奔缠,結(jié)論是:

接口類型的變量存儲(chǔ)的是一對(duì)數(shù)據(jù)

  1. 變量實(shí)際的值
  2. 變量的靜態(tài)類型

例子:

var r io.Reader
tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
if err != nil {
    return nil, err
}
r = tty

r是接口類型變量,保存了值tty和tty的類型*os.File吼野,所以才能使用類型斷言判斷r保存的值的靜態(tài)類型:

var w io.Writer
w = r.(io.Writer)

雖然r中包含了tty和它的類型校哎,包含了tty的所有函數(shù),但r是接口類型瞳步,決定了r只能調(diào)用接口io.Reader中包含的函數(shù)闷哆。

記住:接口變量保存的不是接口類型的值单起,還是英語說起來更方便:Interfaces do not hold interface values.

反射的3條定律

定律1:從接口值到反射對(duì)象

反射是一種檢測(cè)存儲(chǔ)在接口變量中值和類型的機(jī)制抱怔。通過reflect包的一些函數(shù),可以把接口轉(zhuǎn)換為反射定義的對(duì)象嘀倒。

掌握reflect包的以下函數(shù):

  1. reflect.ValueOf({}interface) reflect.Value:獲取某個(gè)變量的值屈留,但值是通過reflect.Value對(duì)象描述的。
  2. reflect.TypeOf({}interface) reflect.Type:獲取某個(gè)變量的靜態(tài)類型测蘑,但值是通過reflect.Type對(duì)象描述的灌危,是可以直接使用Println打印的。
  3. reflect.Value.Kind() Kind:獲取變量值的底層類型(類別)碳胳,注意不是類型勇蝙,是Int、Float固逗,還是Struct浅蚪,還是Slice藕帜,具體見此烫罩。
  4. reflect.Value.Type() reflect.Type:獲取變量值的類型,效果等同于reflect.TypeOf洽故。

再解釋下Kind和Type的區(qū)別贝攒,比如:

type MyInt int
var x MyInt = 7
v := reflect.ValueOf(x)

v.Kind()得到的是Int,而Type得到是MyInt时甚。

定律2:從反射對(duì)象到接口值

定律2是定律1的逆向過程隘弊,上面我們學(xué)了:普通變量 -> 接口變量 -> 反射對(duì)象的過程哈踱,這是從反射對(duì)象 -> 接口變量的過程,使用的是ValueInterface函數(shù)梨熙,是把實(shí)際的值賦值給空接口變量开镣,它的聲明如下:

func (v Value) Interface() (i interface{})

回憶一下:接口變量存儲(chǔ)了實(shí)際的值和值的類型,Println可以根據(jù)接口變量實(shí)際存儲(chǔ)的類型自動(dòng)識(shí)別其值并打印咽扇。

注意事項(xiàng):如果Value是結(jié)構(gòu)體的非導(dǎo)出字段邪财,調(diào)用該函數(shù)會(huì)導(dǎo)致panic。

定律3:當(dāng)反射對(duì)象所存的值是可設(shè)置時(shí)质欲,反射對(duì)象才可修改

從定律1入手理解树埠,定律3就不再那么難懂。

Settability is a property of a reflection Value, and not all reflection Values have it.

可設(shè)置指的是嘶伟,可以通過Value設(shè)置原始變量的值怎憋。

通過函數(shù)的例子思考一下可設(shè)置:

func f(x int)

在調(diào)用f的時(shí)候,傳入了參數(shù)x九昧,從函數(shù)內(nèi)部修改x的值绊袋,外部的變量的值并不會(huì)發(fā)生改變,因?yàn)檫@種是傳值铸鹰,是拷貝的傳遞方式愤炸。

func f(p *int)

函數(shù)f的入?yún)⑹侵羔橆愋停诤瘮?shù)內(nèi)部的修改變量的值掉奄,函數(shù)外部變量的值也會(huì)跟著變化规个。

使用反射也是這個(gè)原理,如果創(chuàng)建Value時(shí)傳遞的是變量姓建,則Value是不可設(shè)置的诞仓。如果創(chuàng)建Value時(shí)傳遞的是變量地址,則Value是可設(shè)置的速兔。

可以使用Value.CanSet()檢測(cè)是否可以通過此Value修改原始變量的值墅拭。

x := 10
v1 := reflect.ValueOf(x)
fmt.Println("setable:", v1.CanSet())
p := reflect.ValueOf(&x)
fmt.Println("setable:", p.CanSet())
v2 := p.Elem()
fmt.Println("setable:", v2.CanSet())

如何通過Value設(shè)置原始對(duì)象值呢?

Value.SetXXX()系列函數(shù)可設(shè)置Value中原始對(duì)象的值涣狗。

系列函數(shù)有:

  • Value.SetInt()
  • Value.SetUint()
  • Value.SetBool()
  • Value.SetBytes()
  • Value.SetFloat()
  • Value.SetString()
  • ...

設(shè)置函數(shù)這么多谍婉,到底該選用哪個(gè)Set函數(shù)?
根據(jù)Value.Kind()的結(jié)果去獲得變量的底層類型镀钓,然后選用該類別的Set函數(shù)穗熬。

參考資料

  1. https://blog.golang.org/laws-of-reflection
  1. 如果這篇文章對(duì)你有幫助,請(qǐng)點(diǎn)個(gè)贊/喜歡丁溅,感謝唤蔗。
  2. 本文作者:大彬
  3. 如果喜歡本文,隨意轉(zhuǎn)載,但請(qǐng)保留此原文鏈接:http://lessisbetter.site/2019/02/24/go-law-of-reflect/
image
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末妓柜,一起剝皮案震驚了整個(gè)濱河市箱季,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌棍掐,老刑警劉巖藏雏,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異作煌,居然都是意外死亡诉稍,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門最疆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來杯巨,“玉大人,你說我怎么就攤上這事努酸》” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵获诈,是天一觀的道長仍源。 經(jīng)常有香客問我,道長舔涎,這世上最難降的妖魔是什么笼踩? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮亡嫌,結(jié)果婚禮上嚎于,老公的妹妹穿的比我還像新娘。我一直安慰自己挟冠,他們只是感情好于购,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著知染,像睡著了一般肋僧。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上控淡,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天嫌吠,我揣著相機(jī)與錄音,去河邊找鬼掺炭。 笑死辫诅,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的竹伸。 我是一名探鬼主播泥栖,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼勋篓!你這毒婦竟也來了吧享?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤譬嚣,失蹤者是張志新(化名)和其女友劉穎钢颂,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體拜银,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡殊鞭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了尼桶。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片操灿。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖泵督,靈堂內(nèi)的尸體忽然破棺而出趾盐,到底是詐尸還是另有隱情,我是刑警寧澤小腊,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布救鲤,位于F島的核電站,受9級(jí)特大地震影響秩冈,放射性物質(zhì)發(fā)生泄漏本缠。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一入问、第九天 我趴在偏房一處隱蔽的房頂上張望丹锹。 院中可真熱鬧,春花似錦芬失、人聲如沸卷仑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽锡凝。三九已至,卻和暖如春垢啼,著一層夾襖步出監(jiān)牢的瞬間窜锯,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來泰國打工芭析, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留锚扎,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓馁启,卻偏偏與公主長得像驾孔,于是被迫代替她去往敵國和親芍秆。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 第一次知道反射的時(shí)候還是許多年前在學(xué)校里玩 C# 的時(shí)候翠勉。那時(shí)總是弄不清楚這個(gè)復(fù)雜的玩意能有什么實(shí)際用途……然后發(fā)...
    勿以浮沙筑高臺(tái)閱讀 1,128評(píng)論 0 9
  • [TOC] Golang的反射reflect深入理解和示例 【記錄于2018年2月】 編程語言中反射的概念 在計(jì)算...
    AllenWu閱讀 868評(píng)論 1 14
  • 【三件事】 1.出發(fā)去北京布展妖啥。 2.問題反饋公司。 3.和寶寶視頻对碌。 【小確幸】 1.下雨了有傘荆虱。 2.布展卸貨...
    晴雨娃閱讀 90評(píng)論 0 0
  • 上班后時(shí)不時(shí)有那么幾天心里堵得慌,心煩意亂朽们,莫名焦躁怀读。今天跟朋友聊了下這種無所適從的心情,我倆就發(fā)現(xiàn)這個(gè)事兒根源在...
    小蔥等豆腐閱讀 550評(píng)論 2 4
  • 春晨風(fēng)光欲喧嘩骑脱, 冬眠冰霜求暖和菜枷。 樓外青梅歡竹馬, 閣內(nèi)知音悲伯樂
    寫作家閱讀 112評(píng)論 0 0