這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
在go語言中,介面賦值分為2中情況:
1、將對象執行個體賦值給介面;
2、將一個介面賦值給另一個介面。
1、將對象執行個體賦值給介面:
要求對象實現了介面的所有方法。
2、將介面賦值給另一個介面:
假設介面A中定義的所有方法,都在介面B中有定義,那麼B介面的執行個體可以賦值給A的對象。反之不成立,除非A和B定義的方法完全一樣(順序不要求),這時A和B等價,可以相互賦值。
3、類執行個體化時指標或者執行個體區別:
執行個體化類的時候,一般有2個方法:
(1) 用new(structName):這個方法得到的是*structName類型,即類的指標類型;
(2) 用structName{init para}:這個方法得到的是structName類型,即類的執行個體類型,不是指標。
注意:
類在實現介面的時候,要注意定義的時候,一般用類的指標傳入,就可以了。因為如果用類的結構體的話,可能會導致“類沒有實現介面中某個方法”的錯誤。詳細舉例:
packagemain
import"fmt"
typetmpInterfaceinterface{
tmpFunc()
tmpFuncPtr()
}
typetmpStructstruct{
}
func(ttmpStruct)tmpFunc(){
fmt.Println("functmpFunc")
}
func(t*tmpStruct)tmpFuncPtr(){
fmt.Println("functmpFuncPtr")
}
funcmain(){
/*這裡如果用new,則得到的是tmpStruct的指標,tmpInterface的2個方法都可用*/
vart_ttmpInterface=new(tmpStruct)
t_t.tmpFunc()
t_t.tmpFuncPtr()
/*這裡如果用tmpStruct{},則得到的是tmpStruct的執行個體,tmpInterface的2個方法都可用*/
/*
vart_ptrtmpInterface=tmpStruct{}
t_ptr.tmpFunc()
t_ptr.tmpFuncPtr()
*/
}
上面的main()函數中,如果用tmpStruct{}初始化(可以理解為建構函式,沒有入參),得到的是 tmpStruct的類型,會編譯出錯:
cannot use tmpStruct literal(type tmpStruct) as type tmpInterface in assignment:tmpStruct does not implementtmpInterface(tmpFuncPtr method has pointer receiver)
個人理解:
受之前對C++或者JAVA等物件導向語言的影響,我們會把定義類的代碼(以及其方法)視為一體的,下面把代碼再複製一份下來:
typetmpStructstruct{
}
func(ttmpStruct)tmpFunc(){
fmt.Println("functmpFunc")
}
func(t*tmpStruct)tmpFuncPtr(){
fmt.Println("functmpFuncPtr")
}
如果把上面這些代碼理解成C++中對類tmpStruct的定義,函數tmpFunc和tmpFuncPtr是類的2個成員函數,那麼會難以理解為什麼又會提示tmpStruct沒有實現tmpFuncPtr這個方法的提示。
其實go語言中,interface和struct之間的關係和傳統的物件導向中的是非常不一樣的,介面和類之間耦合度非常低。同時,對其之間關係起到串連作用的“成員函數”,個人感覺和類以及介面的耦合度也是非常低的,倒不如直接把這些“成員函數”直接理解成普通的方法,只是go語言中允許這樣的特殊文法形式定義:
關鍵字 傳入的類/類指標 正常的函數定義
func (t tmpStruct) tmpFunc() {…}
func (t *tmpStruct) tmpFuncPtr(){…}
在用類的執行個體/執行個體指標初始化介面的對象時:
vart_ttmpInterface=new(tmpStruct)
由於new返回的是一個指標,所以這裡相當於t_t是一個*tmpStruct類型的變數,所以在調用函數的時候,給函數傳入的就是一個指標。
摘抄一句《Go語言編程》裡面的話(3.5.3:介面賦值):
Go語言可以根據下面的函數:
func (a Integer) Less(b Integer) bool
自動產生一個新的Less()方法:
func (a *Integer) Less(b Integer) bool {…}
就是說,有了結構體類型作為入參的函數,go語言會自動建立一個相應的指標作為入參的函數,但是只有指標入參的函數,不會建立相應的結構體類型的函數。
如果這樣定義:
vart_ptrtmpInterface=tmpStruct{}
tmpStruct{}的結果是tmpStruct類型,如果允許賦值給t_ptr成功的話,那麼調用t_ptr.tmpFuncPtr時因為傳入類型錯誤,所以這個角度看,go直接讓編譯不通過也是可以理解的。
總結:如果類的“成員函數”中有傳入結構體指標類型的,則必須用結構體指標類型執行個體化介面的執行個體。