1、反射可以在運(yùn)行時(shí) 動(dòng)態(tài)獲取變量的各種信息 ,比如變量的類型、類別;
10年積累的網(wǎng)站制作、成都網(wǎng)站設(shè)計(jì)經(jīng)驗(yàn),可以快速應(yīng)對(duì)客戶對(duì)網(wǎng)站的新想法和需求。提供各種問(wèn)題對(duì)應(yīng)的解決方案。讓選擇我們的客戶得到更好、更有力的網(wǎng)絡(luò)服務(wù)。我雖然不認(rèn)識(shí)你,你也不認(rèn)識(shí)我。但先網(wǎng)站設(shè)計(jì)后付款的網(wǎng)站建設(shè)流程,更有青山免費(fèi)網(wǎng)站建設(shè)讓你可以放心的選擇與我們合作。
2、如果是結(jié)構(gòu)體變量,還可以獲取到結(jié)構(gòu)體本身的信息(包括結(jié)構(gòu)體的字段、方法);
3、通過(guò)反射,可以修改 變量的值 ,可以調(diào)用關(guān)聯(lián)的方法;
4、使用反射,需要import " reflect ".
5、示意圖:
1、不知道接口調(diào)用哪個(gè)函數(shù),根據(jù)傳入?yún)?shù)在運(yùn)行時(shí)確定調(diào)用的具體接口,這種需要對(duì)函數(shù)或方法反射。
例如以下這種橋接模式:
示例第一個(gè)參數(shù)funcPtr以接口的形式傳入函數(shù)指針,函數(shù)參數(shù)args以可變參數(shù)的形式傳入,bridge函數(shù)中可以用反射來(lái)動(dòng)態(tài)執(zhí)行funcPtr函數(shù)。
1、reflect.TypeOf(變量名),獲取變量的類型,返回reflect.Type類型。
2、reflect.ValueOf(變量名),獲取變量的值,返回reflect.Value類型reflect.Value是一個(gè)結(jié)構(gòu)體類型。
3、變量、interface{}和reflect.Value是可以互相轉(zhuǎn)換的,這點(diǎn)在實(shí)際開(kāi)發(fā)中,會(huì)經(jīng)常使用到。
1、reflect.Value.Kind,獲取變量的 類別(Kind) ,返回的是一個(gè) 常量 。在go語(yǔ)言文檔中:
示例如下所示:
輸出如下:
Kind的范疇要比Type大。比如有Student和Consumer兩個(gè)結(jié)構(gòu)體,他們的 Type 分別是 Student 和 Consumer ,但是它們的 Kind 都是 struct 。
2、Type是類型,Kind是類別,Type和Kind可能是相同的,也可能是不同的。
3、通過(guò)反射可以在讓 變量 在 interface{} 和 Reflect.Value 之間相互轉(zhuǎn)換,這點(diǎn)在前面畫過(guò)示意圖。
4、使用反射的方式來(lái)獲取變量的值(并返回對(duì)應(yīng)的類型),要求數(shù)據(jù)類型匹配,比如x是int,那么久應(yīng)該使用reflect.Value(x).Int(),而不能使用其它的,否則報(bào)panic。
如果是x是float類型的話,也是要用reflect.Value(x).Float()。但是如果是struct類型的話,由于type并不確定,所以沒(méi)有相應(yīng)的方法,只能 斷言。
5、通過(guò)反射的來(lái)修改變量,注意當(dāng)使用SetXxx方法來(lái)設(shè)置需要通過(guò)對(duì)應(yīng)的指針類型來(lái)完成,這樣才能改變傳入的變量的值,同時(shí)需要使用到reflect.Value.Elem()方法。
輸出num=20,即成功使用反射來(lái)修改傳進(jìn)來(lái)變量的值。
6、reflect.Value.Elem()應(yīng)該如何理解?
有沒(méi)有覺(jué)得,發(fā)展到現(xiàn)在,軟件開(kāi)發(fā)行業(yè)是越來(lái)越成熟了,無(wú)論是過(guò)程管理、架構(gòu)方法、設(shè)計(jì)方法,還是語(yǔ)言、平臺(tái)、框架、工具等,都發(fā)展到了一個(gè)前所未有的高度,相關(guān)思想和理念也日臻完善,我們真正進(jìn)入了一個(gè)最好的時(shí)代。
單就編程語(yǔ)言來(lái)說(shuō),近些年包括Scala(2003)、Groovy(2003)、Go(2009)、Kotlin(2011)、Swift(2014)等新興編程語(yǔ)言如雨后春筍版涌現(xiàn)出來(lái),也給我們帶來(lái)了很多讓人眼前一亮的編程特性,甚至Java這等老牌編程語(yǔ)言也是不斷推陳出新,編程再也不像過(guò)去那般枯燥。
本篇就帶大家一起感受一下現(xiàn)代編程語(yǔ)言那些激動(dòng)人心的特性。
這個(gè)特性其實(shí)有點(diǎn)早了,但是也是很早就讓人感動(dòng)的語(yǔ)言特性了,熟悉Javascript的同學(xué)應(yīng)該對(duì)它很了解。Javascript語(yǔ)言具有動(dòng)態(tài)性,我們可以隨時(shí)為類的某個(gè)實(shí)例添加方法,也可以利用動(dòng)態(tài)原型,為類的所有實(shí)例添加方法,有沒(méi)有感覺(jué)擴(kuò)展類的實(shí)現(xiàn)變得非常方便了呢?
擴(kuò)展和原型很像,允許我們?cè)诓恍薷幕蚶^承類的情況下,將新的函數(shù)方法添加到原類中。這個(gè)特性較早見(jiàn)于C#這門語(yǔ)言,目前在Kotlin、Swift中均可以看到。這里順便說(shuō)一下C#,當(dāng)時(shí)C#出來(lái)的時(shí)候,不得不說(shuō)很多特性是非常棒的,包括擴(kuò)展方法、泛型、分部類等等,比Java好不要太多。像Kotlin,不僅可以擴(kuò)展類的方法,還可以擴(kuò)展類的屬性。
前兩個(gè)都是關(guān)于擴(kuò)展代碼的,這里再來(lái)一個(gè)。我們知道Java 1.8以來(lái),接口interface里的方法可以有自己的默認(rèn)實(shí)現(xiàn)了,大大方便了實(shí)現(xiàn)類,減少了重復(fù)代碼。相對(duì)于Java的這個(gè)實(shí)現(xiàn)是顯示的,Go語(yǔ)言的接口實(shí)現(xiàn)可以是隱式的,添加隱式實(shí)現(xiàn)后,所有繼承的結(jié)構(gòu)(Go沒(méi)有類,都是結(jié)構(gòu)struct)都可以調(diào)用這個(gè)方法,和前面的兩個(gè)特性有異曲同工之妙,下面我們對(duì)比看一下。
C語(yǔ)言就有宏的概念,通過(guò) #define 定義,然后在代碼中進(jìn)行替換。宏作為Rust語(yǔ)言的高級(jí)特性,可以操作語(yǔ)法單元,是一種通過(guò)編寫代碼來(lái)生成代碼的方式,被稱作“元編程”(meta programming)。相對(duì)于函數(shù),宏可以接受任意多個(gè)參數(shù),可以減少重復(fù)代碼,定義DSL。宏語(yǔ)法比較復(fù)雜,難以編寫和調(diào)試,以至于在Rust文檔中說(shuō),宏將是其最后的特性。
當(dāng)你回想寫代碼枯燥的時(shí)候,應(yīng)該會(huì)想到為字段編寫getter、setter吧?較早的時(shí)候,C#就意識(shí)到了這個(gè)問(wèn)題,貼心地推出了自動(dòng)屬性這個(gè)語(yǔ)法糖。而Java開(kāi)發(fā)者則是通過(guò)Eclipse、IDEA這樣的開(kāi)發(fā)工具來(lái)自動(dòng)生成getter、setter代碼。當(dāng)然,現(xiàn)在也可以依賴Lombook包,使用lombok的注解@Getter @Setter來(lái)編譯時(shí)生成相關(guān)代碼。
據(jù)說(shuō)空指針異常是軟件業(yè)最貴的異常,價(jià)值10億美元。你有沒(méi)有為處理調(diào)用鏈中的null值而煩惱過(guò)?又或者被傷害過(guò)?Kotlin會(huì)在編譯期提示對(duì)可能為null變量的不安全使用,也提供了Elvis 操作符 ?: 來(lái)方便地處理null值。而有了可選鏈,就舒服多了。可選鏈語(yǔ)法應(yīng)該較早出現(xiàn)在JavaScript語(yǔ)言中,新興語(yǔ)言Swift也提供了這一省心的特性。Swift英明地決定變量是不允許直接存儲(chǔ)NIL值,當(dāng)然也提供了optionals的裝箱功能允許將NIL或其它值包裝起來(lái),方便有時(shí)使用。
輸入乃萬(wàn)惡之源,函數(shù)首要的事情就是檢查不規(guī)范和不安全的輸入,這也是衛(wèi)語(yǔ)句的來(lái)歷。Swift語(yǔ)言為此提供了專門的衛(wèi)語(yǔ)句語(yǔ)法,有了它的貼身防護(hù),整個(gè)代碼都干爽多了,劇烈運(yùn)動(dòng)都不怕,不信往下瞧:
如果要評(píng)選最酷的語(yǔ)言特性,那么Lambda表達(dá)式必須獲得提名。Lambda表達(dá)式很早就出現(xiàn)在Lisp語(yǔ)言中,python也有,在后來(lái)的C#語(yǔ)言大放異彩,又一次狠狠地羞辱了不長(zhǎng)進(jìn)的Java,而Java也終于在1.8版本后加入了這一特性,甚至C++ 11也光榮地上車了。
我們知道編程語(yǔ)言有靜態(tài)和動(dòng)態(tài)之分,靜態(tài)語(yǔ)言如Java 、 C# 、 C 和 C++,動(dòng)態(tài)語(yǔ)言如Perl,Python,JavaScript,Ruby 和 PHP等,多數(shù)為腳本語(yǔ)言。而融合了靜態(tài)和動(dòng)態(tài)特性的語(yǔ)音,就被稱為漸進(jìn)式語(yǔ)言,如TypeScript、Common LISP、Dylan、Cecil、Visual Basic.NET、Bigloo Scheme、Strongtalk等。靜態(tài)類型檢查可以盡早地發(fā)現(xiàn) BUG,動(dòng)態(tài)類型檢查可以方便地處理依賴于運(yùn)行時(shí)信息的值的類型。 漸進(jìn)式語(yǔ)言允許類型注釋來(lái)控制程序的一部分使用靜態(tài)類型檢查,而另一部分為動(dòng)態(tài)檢查,更具靈活性。 Python從3.5開(kāi)始引入了對(duì)靜態(tài)類型檢查的支持。
在面向?qū)ο蟮木幊陶Z(yǔ)言中,狀態(tài)是計(jì)算的基礎(chǔ)。由于可變狀態(tài)的存在,在編寫高并發(fā),多線程代碼時(shí),無(wú)法知道并行進(jìn)行的諸多狀態(tài)讀寫中是否有順序上的錯(cuò)誤,而且這種錯(cuò)誤又是難以察覺(jué)的,而不變性則規(guī)避了這個(gè)問(wèn)題。 不變性是函數(shù)式編程的基礎(chǔ),不變性意味著函數(shù)沒(méi)有副作用,無(wú)論多少次執(zhí)行,相同的輸入就意味著相同的輸出,所有線程都可以無(wú)所顧忌的執(zhí)行同一個(gè)函數(shù)的代碼,代碼更像數(shù)學(xué)函數(shù),更易理解和測(cè)試。
String就是構(gòu)建在Java語(yǔ)言內(nèi)核中的不可變類的一個(gè)典型例子。Java 的 CopyOnWrite系列容器類也是利用了不變性增強(qiáng)了并發(fā)安全性。Java可以通過(guò)final修飾符實(shí)現(xiàn)類和變量的不可變。而Scala、Swift、Groovy等語(yǔ)言也有各自的語(yǔ)法實(shí)現(xiàn)不可變的變量和類。
多重分派是一些編程語(yǔ)言的特性,其中的函數(shù)或者方法,可以在運(yùn)行時(shí)間(動(dòng)態(tài)的)使用一個(gè)或多個(gè)實(shí)際參數(shù)的組合特征,路由動(dòng)態(tài)分派至實(shí)現(xiàn)函數(shù)或方法。多重分派主要區(qū)別于我們常見(jiàn)的重載方法,重載方法是在編譯期就綁定了,而多重分派是在運(yùn)行期分派的。Lisp、Julia、C#、Groovy等語(yǔ)言內(nèi)建多分派特性,JavaScript、Python和C等語(yǔ)言通過(guò)擴(kuò)展支持多分派。 多重分派可以避免我們寫很多分支條件,而是更直觀地用對(duì)象類型表達(dá),使代碼變得可讀性更好并且較少發(fā)生錯(cuò)誤。
前面幾個(gè)特性是不是略顯沉悶,那么來(lái)看一下這個(gè)激動(dòng)一下。解構(gòu)這一語(yǔ)法特性用于從數(shù)組索引或?qū)ο髮傩詣?chuàng)建變量,簡(jiǎn)直帥到飛起。
愛(ài)寫單元測(cè)試的同學(xué)有福了,這個(gè)絕壁是重磅炸彈,在生產(chǎn)代碼里夾著測(cè)試代碼,你有想過(guò)這么寫測(cè)試嗎?誰(shuí)想的?簡(jiǎn)直腦洞打開(kāi)啊!該特性在Pyret語(yǔ)言中,Pyret旨在作為編程教育的杰出選擇,同時(shí) 探索 腳本和函數(shù)式編程的融合。
如果內(nèi)聯(lián)測(cè)試沒(méi)有讓你震驚,D語(yǔ)言內(nèi)聯(lián)編譯期的這個(gè)特性絕對(duì)會(huì)讓你驚掉下巴,基于該特性,開(kāi)發(fā)人員可以直接在D語(yǔ)言中嵌入?yún)R編代碼,徹底放飛自我了,俺滴親娘??!受不了!受不了!順便說(shuō)一下,D語(yǔ)言比較小眾,是C++的一個(gè)改進(jìn)型,它包括了按合約設(shè)計(jì)、垃圾回收、關(guān)聯(lián)數(shù)組、數(shù)組切片和惰性求值等特性。
好吧,我們看點(diǎn)其它的來(lái)壓壓驚吧。盡管Kotlin語(yǔ)言也說(shuō)自己實(shí)現(xiàn)了模式匹配,但是實(shí)際上只是一點(diǎn)點(diǎn)帥,真正帥的是 Elixir語(yǔ)言的模式匹配,Elixir作為一種在Erlang OTP上運(yùn)行的動(dòng)態(tài)類型語(yǔ)言,將模式匹配提升到了一個(gè)全新的水平。
在編程語(yǔ)法上,Python真是個(gè)神一樣的存在,for循環(huán)都能寫出花來(lái)。
Java 8 中提供了Stream API特性, Stream 使用一種類似用 SQL 語(yǔ)句從數(shù)據(jù)庫(kù)查詢數(shù)據(jù)的直觀方式,來(lái)提供一種對(duì) Java 集合運(yùn)算和表達(dá)的高階抽象。事實(shí)上這個(gè)特性C#早就有了(Java又躺槍一次)。不得不說(shuō),利用這個(gè)特性寫出來(lái)的代碼,看上去還真的是很流利的。
1、服務(wù)器編程:以前你如果使用C或者C++做的那些事情,用Go來(lái)做很合適,例如處理日志、數(shù)據(jù)打包、虛擬機(jī)處理、文件系統(tǒng)等。
2、分布式系統(tǒng)、數(shù)據(jù)庫(kù)代理器、中間件:例如Etcd。
3、網(wǎng)絡(luò)編程:這一塊目前應(yīng)用最廣,包括Web應(yīng)用、API應(yīng)用、下載應(yīng)用,而且Go內(nèi)置的net/http包基本上把我們平常用到的網(wǎng)絡(luò)功能都實(shí)現(xiàn)了。
4、開(kāi)發(fā)云平臺(tái):目前國(guó)外很多云平臺(tái)在采用Go開(kāi)發(fā),我們所熟知的七牛云、華為云等等都有使用Go進(jìn)行開(kāi)發(fā)并且開(kāi)源的成型的產(chǎn)品。
5、區(qū)塊鏈:目前有一種說(shuō)法,技術(shù)從業(yè)人員把Go語(yǔ)言稱作為區(qū)塊鏈行業(yè)的開(kāi)發(fā)語(yǔ)言。如果大家學(xué)習(xí)區(qū)塊鏈技術(shù)的話,就會(huì)發(fā)現(xiàn)現(xiàn)在有很多很多的區(qū)塊鏈的系統(tǒng)和應(yīng)用都是采用Go進(jìn)行開(kāi)發(fā)的,比如ehtereum是目前知名度最大的公鏈,再比如fabric是目前最知名的聯(lián)盟鏈,兩者都有g(shù)o語(yǔ)言的版本,且go-ehtereum還是以太坊官方推薦的版本。
自1.0版發(fā)布以來(lái),go語(yǔ)言引起了眾多開(kāi)發(fā)者的關(guān)注,并得到了廣泛的應(yīng)用。go語(yǔ)言簡(jiǎn)單、高效、并發(fā)的特點(diǎn)吸引了許多傳統(tǒng)的語(yǔ)言開(kāi)發(fā)人員,其數(shù)量也在不斷增加。
使用 Go 語(yǔ)言開(kāi)發(fā)的開(kāi)源項(xiàng)目非常多。早期的 Go 語(yǔ)言開(kāi)源項(xiàng)目只是通過(guò) Go 語(yǔ)言與傳統(tǒng)項(xiàng)目進(jìn)行C語(yǔ)言庫(kù)綁定實(shí)現(xiàn),例如 Qt、Sqlite 等。
后期的很多項(xiàng)目都使用 Go 語(yǔ)言進(jìn)行重新原生實(shí)現(xiàn),這個(gè)過(guò)程相對(duì)于其他語(yǔ)言要簡(jiǎn)單一些,這也促成了大量使用 Go 語(yǔ)言原生開(kāi)發(fā)項(xiàng)目的出現(xiàn)。
當(dāng)您對(duì)外部模塊的存儲(chǔ)庫(kù)進(jìn)行了 fork (例如修復(fù)模塊代碼中的問(wèn)題或添加功能)時(shí),您可以讓 Go 工具將您的 fork 用于模塊的源代碼。這對(duì)于測(cè)試您自己的代碼的更改很有用。
為此,您可以使用go.mod 文件中的replace指令將外部模塊的原始模塊路徑替換為存儲(chǔ)庫(kù)中 fork 的路徑。這指示 Go 工具在編譯時(shí)使用替換路徑(fork 的位置),例如,同時(shí)允許您保留import 原始模塊路徑中的語(yǔ)句不變。
在以下 go.mod 文件示例中,當(dāng)前模塊需要外部模塊example.com/theirmodule。然后該replace指令將原始模塊路徑替換為example.com/myfork/theirmodule模塊自己的存儲(chǔ)庫(kù)的分支。
設(shè)置require/replace對(duì)時(shí),使用 Go 工具命令確保文件描述的需求保持一致。使用go list命令獲取當(dāng)前模塊正在使用的版本。然后使用go mod edit命令將需要的模塊替換為fork:
注意: 當(dāng)您使用該replace指令時(shí),Go 工具不會(huì)像添加依賴項(xiàng)中所述對(duì)外部模塊進(jìn)行身份驗(yàn)證。
您可以使用go get命令從其存儲(chǔ)庫(kù)中的特定提交為模塊添加未發(fā)布的代碼。
為此,您使用go get命令,用符號(hào)@指定您想要的代碼 。當(dāng)您使用go get時(shí),該命令將向您的 go.mod 文件添加一個(gè) 需要外部模塊的require指令,使用基于有關(guān)提交的詳細(xì)信息的偽版本號(hào)。
以下示例提供了一些說(shuō)明。這些基于源位于 git 存儲(chǔ)庫(kù)中的模塊。
當(dāng)您的代碼不再使用模塊中的任何包時(shí),您可以停止將該模塊作為依賴項(xiàng)進(jìn)行跟蹤。
要停止跟蹤所有未使用的模塊,請(qǐng)運(yùn)行g(shù)o mod tidy 命令。此命令還可能添加在模塊中構(gòu)建包所需的缺失依賴項(xiàng)。
要?jiǎng)h除特定依賴項(xiàng),請(qǐng)使用go get,指定模塊的模塊路徑并附加 @none,如下例所示:
go get命令還將降級(jí)或刪除依賴于已刪除模塊的其他依賴項(xiàng)。
當(dāng)您使用 Go 工具處理模塊時(shí),這些工具默認(rèn)從 proxy.golang.org(一個(gè)公共的 Google 運(yùn)行的模塊鏡像)或直接從模塊的存儲(chǔ)庫(kù)下載模塊。您可以指定 Go 工具應(yīng)該使用另一個(gè)代理服務(wù)器來(lái)下載和驗(yàn)證模塊。
如果您(或您的團(tuán)隊(duì))已經(jīng)設(shè)置或選擇了您想要使用的不同模塊代理服務(wù)器,您可能想要這樣做。例如,有些人設(shè)置了模塊代理服務(wù)器,以便更好地控制依賴項(xiàng)的使用方式。
要為 Go 工具指定另一個(gè)模塊代理服務(wù)器,請(qǐng)將GOPROXY 環(huán)境變量設(shè)置為一個(gè)或多個(gè)服務(wù)器的 URL。Go 工具將按照您指定的順序嘗試每個(gè) URL。默認(rèn)情況下,GOPROXY首先指定一個(gè)公共的 Google 運(yùn)行模塊代理,然后從模塊的存儲(chǔ)庫(kù)直接下載(在其模塊路徑中指定):
您可以將變量設(shè)置為其他模塊代理服務(wù)器的 URL,用逗號(hào)或管道分隔 URL。
Go 模塊經(jīng)常在公共互聯(lián)網(wǎng)上不可用的版本控制服務(wù)器和模塊代理上開(kāi)發(fā)和分發(fā)。您可以設(shè)置 GOPRIVATE環(huán)境變量。您可以設(shè)置GOPRIVATE環(huán)境變量來(lái)配置go命令以從私有源下載和構(gòu)建模塊。然后 go 命令可以從私有源下載和構(gòu)建模塊。
GOPRIVATE或環(huán)境變量可以設(shè)置為匹配模塊前綴的全局模式列表,這些GONOPROXY前綴是私有的,不應(yīng)從任何代理請(qǐng)求。例如: