版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
第Go基礎(chǔ)教程系列之Go接口使用詳解但這些行為不會(huì)在接口上直接實(shí)現(xiàn),而是需要用戶自定義的方法來實(shí)現(xiàn)。所以,在上面的Namer接口類型中的方法my_methodN都是沒有實(shí)際方法體的,僅僅只是在接口Namer中存放這些方法的簽名(簽名=函數(shù)名+參數(shù)(類型)+返回值(類型))。
當(dāng)用戶自定義的類型實(shí)現(xiàn)了接口上定義的這些方法,那么自定義類型的值(也就是實(shí)例)可以賦值給接口類型的值(也就是接口實(shí)例)。這個(gè)賦值過程使得接口實(shí)例中保存了用戶自定義類型實(shí)例。
例如:
packagemain
import(
"fmt"
//Shaper接口類型
typeShaperinterface{
Area()float64
//Circlestruct類型
typeCirclestruct{
radiusfloat64
//Circle類型實(shí)現(xiàn)Shaper中的方法Area()
func(c*Circle)Area()float64{
return3.14*c.radius*c.radius
//Squarestruct類型
typeSquarestruct{
lengthfloat64
//Square類型實(shí)現(xiàn)Shaper中的方法Area()
func(s*Square)Area()float64{
returns.length*s.length
funcmain(){
//Circle類型的指針類型實(shí)例
c:=new(Circle)
c.radius=2.5
//Square類型的值類型實(shí)例
s:=Square{3.2}
//Sharpe接口實(shí)例ins1,它自身是指針類型的
varins1Shaper
//將Circle實(shí)例c賦值給接口實(shí)例ins1
//那么ins1中就保存了實(shí)例c
ins1=c
fmt.Println(ins1)
//使用類型推斷將Square實(shí)例s賦值給接口實(shí)例
ins2:=s
fmt.Println(ins2)
上面將輸出:
{2.5}
{3.2}
從上面輸出結(jié)果中可以看出,兩個(gè)接口實(shí)例ins1和ins2被分別賦值后,分別保存了指針類型的Circle實(shí)例c和值類型的Square實(shí)例s。
另外,從上面賦值ins1和ins2的賦值語句上看:
ins1=c
ins2:=s
是否說明接口實(shí)例ins就是自定義類型的實(shí)例?實(shí)際上接口是指針類型(指向什么見下文)。這個(gè)時(shí)候,自定義類型的實(shí)例c、s稱為具體實(shí)例,ins實(shí)例是抽象實(shí)例,因?yàn)閕ns接口中定義的行為(方法)并沒有具體的行為模式,而c、s中的行為是具體的。
因?yàn)榻涌趯?shí)例ins也是自定義類型的實(shí)例,所以當(dāng)接口實(shí)例中保存了自定義類型的實(shí)例后,就可以直接從接口上調(diào)用它所保存的實(shí)例的方法。例如:
fmt.Println(ins1.Area())//輸出19.625
fmt.Println(ins2.Area())//輸出10.24
這里ins1.Area()調(diào)用的是Circle類型上的方法Area(),ins2.Area()調(diào)用的則是Square類型上的方法Area()。這說明Go的接口可以實(shí)現(xiàn)面向?qū)ο笾械亩鄳B(tài):可以按需調(diào)用名稱相同、功能不同的方法。
接口實(shí)例中存的是什么
前面說了,接口類型是指針類型,但是它到底存放了什么東西?
接口類型的數(shù)據(jù)結(jié)構(gòu)是2個(gè)指針,占用2個(gè)機(jī)器字長。
當(dāng)將類型實(shí)例c賦值給接口實(shí)例ins1后,用println()函數(shù)輸出ins1和c,比較它們的地址:
println(ins1)
println(c)
輸出結(jié)果:
(0x4ceb00,0xc042068058)
0xc042068058
從結(jié)果中可以看出,接口實(shí)例中包含了兩個(gè)地址,其中第二個(gè)地址和類型實(shí)例c的地址是完全相同的。而第二個(gè)地址c是Circle的指針類型實(shí)例,所以ins中的第二個(gè)值也是指針。
ins中的第一個(gè)是指針是什么?它所指向的是一個(gè)內(nèi)部表結(jié)構(gòu)iTable,這個(gè)Table中包含兩部分:第一部分是實(shí)例c的類型信息,也就是*Circle,第二部分是這個(gè)類型(Circle)的方法集,也就是Circle類型的所有方法(此示例中Circle只定義了一個(gè)方法Area())。
所以,如圖所示:
注意,上圖中的實(shí)例c是指針,是指針類型的Circle實(shí)例。
對于值類型的Square實(shí)例s,ins2保存的內(nèi)容則如下圖:
實(shí)際上接口實(shí)例中保存的內(nèi)容,在反射(reflect)中體現(xiàn)的淋漓盡致,reflect所有的一切都離不開接口實(shí)例保存的內(nèi)容。
方法集(MethodSet)規(guī)則
官方手冊對MethodSet的解釋:/ref/spec#Method_sets
實(shí)例的methodset決定了它所實(shí)現(xiàn)的接口,以及通過receiver可以調(diào)用的方法。
方法集是類型的方法集合,對于非接口類型,每個(gè)類型都分兩個(gè)MethodSet:值類型實(shí)例是一個(gè)MethodSet,指針類型的實(shí)例是另一個(gè)MethodSet。兩個(gè)MethodSet由不同receiver類型的方法組成:
實(shí)例的類型receiver
--------------------------------------
值類型:T(TType)
指針類型:*T(TType)或(T*Type)
也就是說:
值類型的實(shí)例的MethodSet只由值類型的receiver(TType)組成指針類型的實(shí)例的MethodSet由值類型和指針類型的receiver共同組成,即(TType)和(T*Type)
這是什么意思呢?從receiver的角度去考慮:
receiver實(shí)例的類型
---------------------------
(TType)T或*T
(T*Type)*T
上面的意思是:
receiver是指針類型的方法只可能存在于指針類型的實(shí)例方法集中receiver是值類型的方法既存在于值類型的實(shí)例方法集中,也存在于指針類型的方法集中
從實(shí)現(xiàn)接口方法的角度上看:
如果某類型實(shí)現(xiàn)接口的方法的receiver是(T*Type)類型的,那么只有指針類型的實(shí)例*T才算是實(shí)現(xiàn)了這個(gè)接口,因?yàn)檫@個(gè)方法不在值類型的實(shí)例T方法集中如果某類型實(shí)現(xiàn)接口的方法的receiver是(TType)類型的,那么值類型的實(shí)例T和指針類型的實(shí)例*T都算實(shí)現(xiàn)了這個(gè)接口,因?yàn)檫@個(gè)方法既在值類型的實(shí)例T方法集中,也在指針類型的實(shí)例*T方法集中
舉個(gè)例子。接口方法Area(),自定義類型Circle有一個(gè)receiver類型為(c*Circle)的Area()方法時(shí),說明實(shí)現(xiàn)了接口的方法,但只有Circle實(shí)例的類型為指針類型時(shí),這個(gè)實(shí)例才算是實(shí)現(xiàn)了接口,才能賦值給接口實(shí)例,才能當(dāng)作一個(gè)接口參數(shù)。如下:
packagemain
import"fmt"
//Shaper接口類型
typeShaperinterface{
Area()float64
//Circlestruct類型
typeCirclestruct{
radiusfloat64
//Circle類型實(shí)現(xiàn)Shaper中的方法Area()
//receiver類型為指針類型
func(c*Circle)Area()float64{
return3.14*c.radius*c.radius
funcmain(){
//聲明2個(gè)接口實(shí)例
varins1,ins2Shaper
//Circle的指針類型實(shí)例
c1:=new(Circle)
c1.radius=2.5
ins1=c1
fmt.Println(ins1.Area())
//Circle的值類型實(shí)例
c2:=Circle{3.0}
//下面的將報(bào)錯(cuò)
ins2=c2
fmt.Println(ins2.Area())
報(bào)錯(cuò)結(jié)果:
cannotusec2(typeCircle)astypeShaper
inassignment:
CircledoesnotimplementShaper(Areamethodhas
pointerreceiver)
它的意思是,Circle值類型的實(shí)例c2沒有實(shí)現(xiàn)Share接口的Area()方法,它的Area()方法是指針類型的receiver。換句話說,值類型的c2實(shí)例的MethodSet中沒有receiver類型為指針的Area()方法。
所以,上面應(yīng)該改成:
ins2=c2
再聲明一個(gè)方法,它的receiver是值類型的。下面的代碼一切正常。
typeSquarestruct{
lengthfloat64
//實(shí)現(xiàn)方法Area(),receiver為值類型
func(sSquare)Area()float64{
returns.length*s.length
funcmain(){
varins3,ins4Shaper
//值類型的Square實(shí)例s1
s1:=Square{3.0}
ins3=s1
fmt.Println(ins3.Area())
//指針類型的Square實(shí)例s2
s2:=new(Square)
s2.length=4.0
ins4=s2
fmt.Println(ins4.Area())
所以,從struct類型定義的方法的角度去看,如果這個(gè)類型的方法有指針類型的receiver方法,則只能使用指針類型的實(shí)例賦值給接口變量,才算是實(shí)現(xiàn)了接口。如果這個(gè)類型的方法全是值類型的receiver方法,則可以隨意使用值類型或指針類型的實(shí)例賦值給接口變量。下面這兩個(gè)對應(yīng)關(guān)系,對于理解很有幫助:
實(shí)例的類型receiver
--------------------------------------
值類型:T(TType)
指針類型:*T(TType)或(T*Type)
receiver實(shí)例的類型
---------------------------
(TType)T或*T
(T*Type)*T
很經(jīng)常的,我們會(huì)直接使用推斷類型的賦值方式(如ins2:=c2)將實(shí)例賦值給一個(gè)變量,我們以為這個(gè)變量是接口的實(shí)例,但實(shí)際上并不一定。正如上面值類型的c2賦值給ins2,這個(gè)ins2將是從c2數(shù)據(jù)結(jié)構(gòu)拷貝而來的另一個(gè)副本數(shù)據(jù)結(jié)構(gòu),并非接口實(shí)例,但這時(shí)通過ins2也能調(diào)用Area()方法:
c2=Circle{3.2}
ins2:=c2
fmt.Println(ins2.Area())//正常執(zhí)行
之所以能調(diào)用,是因?yàn)镃ircle類型中有Area()方法,但這不是通過接口去調(diào)用的。
所以,在使用接口的時(shí)候,應(yīng)當(dāng)盡量使用var先聲明接口類型的實(shí)例,再將類型的實(shí)例賦值給接口實(shí)例(如varins1,ins2Shaper),或者使用ins1:=Shaper(c1)的方式。這樣,如果賦值給接口實(shí)例的類型實(shí)例沒有實(shí)現(xiàn)該接口,將會(huì)報(bào)錯(cuò)。
但是,為什么要限制指針類型的receiver只能是指針類型的實(shí)例的MethodSet呢?
看下圖,假如指針類型的receiver可以組成值類型實(shí)例的MethodSet,那么接口實(shí)例的第二個(gè)指針就必須找到值類型的實(shí)例的地址。但實(shí)際上,并非所有值類型的實(shí)例都能獲取到它們的地址。
哪些值類型的實(shí)例找不到地址?最常見的是那些簡單數(shù)據(jù)類型的別名類型,如果匿名生成它們的實(shí)例,它們的地址就會(huì)被Go徹底隱藏,外界找不到這個(gè)實(shí)例的地址。
例如:
packagemain
import"fmt"
typemyintint
func(m*myint)add()myint{
return*m+1
funcmain(){
fmt.Println(myint(3).add())
以下是報(bào)錯(cuò)信息:找不到myint(3)的地址
abc\abc.go:11:22:cannotcallpointermethodonmyint(3)
abc\abc.go:11:22:cannottaketheaddressofmyint(3)
這里的myint(3)是匿名的myint實(shí)例,它的底層是簡單數(shù)據(jù)類型int,myint(3)的地址會(huì)被徹底隱藏,只會(huì)提供它的值對象3。
普通方法和實(shí)現(xiàn)接口方法的區(qū)別
對于普通方法,無論是值類型還是指針類型的實(shí)例,都能正常調(diào)用,且調(diào)用時(shí)拷貝的內(nèi)容都由receiver的類型決定。
func(TType)method1//值類型receiver
func(T*Type)method2//指針類型receiver
指針類型的receiver決定了無論是值類型還是指針類型的實(shí)例,都拷貝實(shí)例的指針。值類型的receiver決定了無論是值類型還是指針類型的實(shí)例,都拷貝實(shí)例本身。
所以,對于person數(shù)據(jù)結(jié)構(gòu):
typepersonstruct{}
p1:=person{}//值類型的實(shí)例
p2:=new(person)//指針類型的實(shí)例
p1.method1()和p2.method1()都是拷貝整個(gè)person實(shí)例,只不過Go對待p2.method1()時(shí)多一個(gè)步驟:將其解除引用。所以p2.method1()等價(jià)于(*p2).method1()。
p1.method2()和p2.method2()都拷貝person實(shí)例的指針,只不過Go對待p1.method2()時(shí)多一個(gè)步驟:創(chuàng)建一個(gè)額外的引用。所以,p1.method2()等價(jià)于(p1).method2()。
而類型實(shí)現(xiàn)接口方法時(shí),methodset規(guī)則決定了類型實(shí)例是否實(shí)現(xiàn)了接口。
receiver實(shí)例的類型
---------------------------
(TType)T或*T
(T*Type)*T
對于接口abc、接口方法method1()、method2()和結(jié)構(gòu)person:
typeabcinterface{
method1
method2
typepersonstruct{}
func(Tperson)method1//值類型receiver
func(T*person)method2//指針類型receiver
p1:=abc(person)//接口變量保存值類型實(shí)例
p2:=abc(person)//接口變量保存指針類型實(shí)例
p2.method1()、p2.method2()以及p1.method1()都是允許的,都會(huì)通過接口實(shí)例去調(diào)用具體person實(shí)例的方法。
但p1.method2()是錯(cuò)誤的,因?yàn)閙ethod2()的receiver是指針類型的,導(dǎo)致p1沒有實(shí)現(xiàn)接口abc的method2()方法。
接口類型作為參數(shù)
將接口類型作為參數(shù)很常見。這時(shí),那些實(shí)現(xiàn)接口的實(shí)例都能作為接口類型參數(shù)傳遞給函數(shù)/方法。
例如,下面的myArea()函數(shù)的參數(shù)是nShaper,是接口類型。
packagemain
import(
"fmt"
//Shaper接口類型
typeShaperinterface{
Area()float64
//Circlestruct類型
typeCirclestruct{
radiusfloat64
//Circle類型實(shí)現(xiàn)Shaper中的方法Area()
func(c*Circle)Area()float64{
return3.14*c.radius*c.radius
funcmain(){
//Circle的指針類型實(shí)例
c1:=new(Circle)
c1.radius=2.5
myArea(c1)
funcmyArea(nShaper){
fmt.Println(n.Area())
上面myArea(c1)是將c
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 酒店服務(wù)品牌建設(shè)-洞察及研究
- 超聲波傳感器在醫(yī)療健康監(jiān)測中的創(chuàng)新應(yīng)用-洞察及研究
- 高性能果蔬纖維復(fù)合材料-洞察及研究
- 2026年網(wǎng)絡(luò)安全工程師認(rèn)證測試題
- 2026年工業(yè)自動(dòng)化技術(shù)考試題目
- 2026年電氣工程基礎(chǔ)理論試題
- 2026年工程領(lǐng)域基礎(chǔ)知識(shí)與技能評(píng)估試題
- 突破自我2026年市場營銷人員市場洞察與策略調(diào)整試題
- 專業(yè)升級(jí)2026年職稱評(píng)審專業(yè)測試題庫
- 軟件開發(fā)工程師考試題庫代碼優(yōu)化技巧高效編程實(shí)踐2026
- GB/T 17213.4-2015工業(yè)過程控制閥第4部分:檢驗(yàn)和例行試驗(yàn)
- FZ/T 73009-2021山羊絨針織品
- JJF 1069-2012 法定計(jì)量檢定機(jī)構(gòu)考核規(guī)范(培訓(xùn)講稿)
- GB∕T 5900.2-2022 機(jī)床 主軸端部與卡盤連接尺寸 第2部分:凸輪鎖緊型
- 2011-2015廣汽豐田凱美瑞維修手冊wdl
- DFMEA編制作業(yè)指導(dǎo)書新版
- KTronics籃輸送式洗碗碟機(jī)操作手冊
- DB35∕T 1844-2019 高速公路邊坡工程監(jiān)測技術(shù)規(guī)程
- 城市管理綜合執(zhí)法局城管執(zhí)法與執(zhí)法程序PPT模板
- 閥門基礎(chǔ)知識(shí)上
- 第二章注射成型工藝與模具結(jié)構(gòu)
評(píng)論
0/150
提交評(píng)論