這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
1.匿名組合
Go語言也提供了繼承,但是採用了組合的方式,所以我們將其稱為匿名組合:
package mainimport "fmt"//定義基類type Base struct { Name string}//基類相關的2個成員方法func (base *Base) A() { fmt.Println("Base method A called...")}func (base *Base) B() { fmt.Println("Base method B called...")}//定義子類type Son struct { Base //"繼承"基類}//重寫基類的B方法func (son *Son) B() { son.Base.B() //調用基類的B方法 fmt.Println("Son method B called...")}func main(){ son := Son{Base{"mChenys"}} son.B() //調用子類的重寫至基類的B方法 son.A() //調用子類繼承至基類的A方法}
輸出結果:
Base method B called...Son method B called...Base method A called...
以上代碼定義了一個Base類(實現了A()和B()兩個成員方法),然後定義了一個
Son,該類從Base類“繼承”並改寫了B()方法(該方法實現時先調用了基類的B()方法).
在“衍生類別”Son沒有改寫“基類”Base的成員方法時,相應的方法就被“繼承”,例如在
上面的例子中,調用son.A()和調用son.Base.A()效果一致。
與其他語言不同,Go語言很清晰地告訴你類的記憶體布局是怎樣的。此外,在Go語言中你還
可以隨心所欲地修改記憶體布局,如:
type Son struct { ... // 其他成員 Base}
這段代碼從語義上來說,和上面給的例子並無不同,但記憶體布局發生了改變。“基類” Base
的資料放在了“衍生類別” Son的最後。
另外,在Go語言中,你還可以以指標方式從一個類型“派生”:
type Son struct {
*Base
…// 其他成員
}
這段Go代碼仍然有“派生”的效果,只是Son建立執行個體的時候,需要外部提供一個Base類
執行個體的指標.
如下所示,匿名組合了一個log.Logger指標:
type Job struct { Command string *log.Logger}
在合適的賦值後,我們在Job類型的所有成員方法中可以很舒適地借用所有log.Logger提
供的方法。比如如下的寫法:
func (job *Job)Start() {
job.Log(“starting now…”) //注意:Log函數的接收者仍然是log.Logger指標
… // 做一些事情
job.Log(“started.”)
}
對於Job的實現者來說,他甚至根本就不用意識到log.Logger類型的存在,這就是匿名組合的
魅力所在.在實際工作中,只有合理利用才能最大發揮這個功能的價值。
2.名字衝突問題
我們必須關注一下介面組合中的名字衝突問題,比如如下的組合:
package mainimport( "fmt")type X struct { Name string}type Y struct { X Name string //相同名字的屬性名稱會覆蓋父類的屬性}func main(){ y := Y{X{"XChenys"},"YChenys"} fmt.Println("y.Name = ",y.Name) //y.Name = YChenys}
組合的類型和被組合的類型都包含一個Name成員,會不會有問題呢?答案是否定的。所有
的Y類型的Name成員的訪問都只會訪問到最外層的那個Name變數,X.Name變數相當於被覆蓋了。