因為結構Student和Teacher實現(xiàn)接口Human的方法SayHello時,接受的是通過一個指針類型的變量(見(s *Student)和(t *Teacher))來調用這個方法。因此,在調用SayHi函數時,只能傳遞Student或Teacher的對象的地址,傳遞它們的對象是錯的。
創(chuàng)新互聯(lián)公司長期為千余家客戶提供的網站建設服務,團隊從業(yè)經驗10年,關注不同地域、不同群體,并針對不同對象提供差異化的產品和服務;打造開放共贏平臺,與合作伙伴共同營造健康的互聯(lián)網生態(tài)環(huán)境。為東河企業(yè)提供專業(yè)的成都做網站、成都網站建設,東河網站改版等技術服務。擁有十年豐富建站經驗和眾多成功案例,為您定制開發(fā)。
相反,如果結構Student和Teacher實現(xiàn)接口Human的方法SayHello時,接受的是通過一個對象(像(s Student)和(t Teacher))來調用這個方法。則在調用SayHi函數時,既能傳遞Student或Teacher的對象,也能傳遞Student或Teacher的對象的地址。
GO是編譯性語言,所以函數的順序是無關緊要的,為了方便閱讀,建議入口函數 main 寫在最前面,其余函數按照功能需要進行排列
GO的函數 不支持嵌套,重載和默認參數
GO的函數 支持 無需聲明變量,可變長度,多返回值,匿名,閉包等
GO的函數用 func 來聲明,且左大括號 { 不能另起一行
一個簡單的示例:
輸出為:
參數:可以傳0個或多個值來供自己用
返回:通過用 return 來進行返回
輸出為:
上面就是一個典型的多參數傳遞與多返回值
對例子的說明:
按值傳遞:是對某個變量進行復制,不能更改原變量的值
引用傳遞:相當于按指針傳遞,可以同時改變原來的值,并且消耗的內存會更少,只有4或8個字節(jié)的消耗
在上例中,返回值 (d int, e int, f int) { 是進行了命名,如果不想命名可以寫成 (int,int,int){ ,返回的結果都是一樣的,但要注意:
當返回了多個值,我們某些變量不想要,或實際用不到,我們可以使用 _ 來補位,例如上例的返回我們可以寫成 d,_,f := test(a,b,c) ,我們不想要中間的返回值,可以以這種形式來舍棄掉
在參數后面以 變量 ... type 這種形式的,我們就要以判斷出這是一個可變長度的參數
輸出為:
在上例中, strs ...string 中, strs 的實際值是b,c,d,e,這就是一個最簡單的傳遞可變長度的參數的例子,更多一些演變的形式,都非常類似
在GO中 defer 關鍵字非常重要,相當于面相對像中的析構函數,也就是在某個函數執(zhí)行完成后,GO會自動這個;
如果在多層循環(huán)中函數里,都定義了 defer ,那么它的執(zhí)行順序是先進后出;
當某個函數出現(xiàn)嚴重錯誤時, defer 也會被調用
輸出為
這是一個最簡單的測試了,當然還有更復雜的調用,比如調試程序時,判斷是哪個函數出了問題,完全可以根據 defer 打印出來的內容來進行判斷,非??焖?,這種留給你們去實現(xiàn)
一個函數在函數體內自己調用自己我們稱之為遞歸函數,在做遞歸調用時,經常會將內存給占滿,這是非常要注意的,常用的比如,快速排序就是用的遞歸調用
本篇重點介紹了GO函數(func)的聲明與使用,下一篇將介紹GO的結構 struct
這是它的優(yōu)點,因為編譯器在編譯時不去確定你傳的到底是什么類型,你傳一個string,它能接收,你傳一個對象struct,它也能接收,它只有一個要求,實現(xiàn)我要求實現(xiàn)的方法!
既然interface是不限定類型,是通用類型,這是一種開放表現(xiàn),這種開放怎么實現(xiàn)的呢?方法就是不去檢驗你的類型,既然不檢驗那也不去記錄你的類型?。。?!注意interface不記錄你的類型,所以不管你是string,struct,int,我都不管,我都不記錄,我只記錄你的地址,結果是編譯器在編譯時也不知道你是什么類型,你有什么字段!
但是現(xiàn)在有一個問題,編譯器也沒辦法確定一個interface以前是什么類型?。ň幾g時)這就是因果關系:為了達到通用,interface不做確定工作,結果就是interface也不知道以前的類型。
一個類型轉接口的過程,就是放棄自我類型的過程,變成了沒有類型。
這樣做有什么好處呢,很顯然是:通用,如果把一個函數的傳入參數設置為空接口(interface{}),那么任何類型當做參數都能夠調用該接口,最好的例子就是:
它就是一個很標準的例子,println傳入參數可以是任何類型,都能打印出它的值。
當然你可以說你記得,因為是你把它轉換成interface,你理所當然的記得,可編譯器不知道啊,interface不包含類型,也就是說你沒有讓它去記錄,所以它不知道。
針對這個問題,go語言給了一個解決方案,斷言,當將一個interface轉換成它原來類型的時候,在它后面指明它的原來類型,這樣編譯器就知道該按照什么類型去解析了。(其實說白了,這就是通過人的記憶,編譯器不知道是什么類型,你告訴編譯器就可以了)
斷言其實是先獲取interface的動態(tài)類型,然后與你指定的類型做判斷,如果一致,將它轉換成你指定的類型。如果不知道動態(tài)類型,可以看這篇文章:
從報錯可以看出, 不能直接轉換,需要對接口先進行斷言
通常情況下,一個變量在確定類型的情況下編譯器知道他有哪些功能(注意,這里是針對編譯時),比如一個int類型,編譯器在編譯時知道能對他加減int,不能加減float,如果你這么做我就給你報錯。一個struct包含哪些字段,不包含哪些字段,我定義一個user結構體,里面只有name和age兩個字段,那么你只能取我這兩字段的值,你如果取height,我就給你報錯。
這些都是正常情況下的,但是對于一個接口呢,編譯器會變成瞎子!在編譯的時候它不知道你原來是什么類型,所以它也沒法確定你包含什么字段,同樣是之前那個user結構體,當把它轉換成接口以后,編譯器就對它的類型一無所知了,你獲取name字段,這有接口有沒有呢?編譯器不知道!你請求height字段,這個泛型有沒有呢?編譯器仍然不知道。所以你編譯時不能修改接口里的數據,既然編譯時 不能修改,那就只能在運行時修改了。
這個時候就該反射登場了,它能夠在運行時修改接口的數據,通過追根溯源,獲取接口底層的實際數據和類型,讓你能夠對接口的源數據進行操作。
換一種大白話的說法,反射就是刨根問底,獲取這個接口究竟是怎么產生的,因為哪怕一個類型轉變成接口時放棄了自己的類型,但是它的本質不會變的,就像趙本山的小品里所說:小樣,別以為你脫掉馬甲我就不認識你了!對,它的底層里仍然存儲了它的數據類型,只是藏的比較深,一般手段拿不到,但我們仍然能夠通過反射(這個包根問底的工具)來確定你究竟包含哪些字段和值,確定你究竟是蛇還是脫了馬甲的烏龜!