go中沒有繼承,只能通過組合來實現繼承。
繼承和組合區別
繼承就是子類繼承了父類的特征和行為,使得子類實例具有父類的行為和方法,屬於is-a的范疇。
組合就是通過對現有對象的拼裝從而獲得實現更為復雜的行為的方法。
- 一個struct嵌套了另外一個匿名的struct從而實現了繼承,嵌套多個匿名struct實現多重繼承。
- 一個struct嵌套了寧外一個struct的實例實現了組合。
type Animal struct {
}
//繼承
type Cat struct {
//匿名
*Animail
}
//組合
type Dog struct {
animal Animal
}
繼承的簡單實現
type Animal struct {
name string
}
type MachineCat struct {
*Animal
}
//定義一個receive為IAnimal的函數
func (value *Animal) GetName() {
fmt.Printf("Animal: %v\n", value.name)
}
func main() {
//實例化machineCat
machineCat := &MachineCat{
&Animal{
"test",
},
}
machineCat.GetName()
}
輸出內容
Animal: test
聲明一個struct Animal,然后再申明一個嵌套Animal的MachineCat的struct,再定義一個接受類型為Animal的GetName()的方法,當傳入類型為MachineCat的時候,會先去找有沒有傳入類型為MachineCat的GetName()的方法,沒有找到,就會繼續尋找它的嵌入類型Animal的GetName()的方法。
如果申明一個傳入類型為MachineCat會發生什么情況
在main()前面加入
//定義一個receive為MachineCat的函數
func (value *MachineCat) GetName() {
fmt.Printf("MachineCat: %v\n", value.name)
}
輸出內容
MachineCat:test
可以看到調用的是receive為MachineCat的GetName()方法。
構造函數與多態
其他語言都是通過繼承接口(實現一類行為的方法)來實現多態。
type IAnimal interface {
GetName()
}
type Animal struct {
Name string
}
func NewAnimal(name string) *Animal{
return &Animal{
Name: name,
}
}
func (a *Animal) GetName() {
fmt.Printf("Animal name: %v\n", a.Name)
}
type MachineCat struct {
* Animal
}
func newMachineCat(name string) *MachineCat {
return &MachineCat{
NewAnimal(name),
}
}
func main() {
//實例化machineCat
machineCat := newMachineCat("newMachineCat")
var animal IAnimal = machineCat
animal.GetName()
}
在go中,構造函數實際上就是一個返回為struct的普通函數。
首先定義一個IAnimal interface的接口,接口中只有一個聲明為GetName()的方法。分別定義Animal, MachineCat的struct以及他們的構造方法。 只需要在外面傳入參數,就可以生成各自的實例。在main()方法中,構造一個MachineCat賦值給接口對象var animal IAnimal就可以在接口中實現多態。如果調用MachineCat的GetName()的方法:
//receive類型為MachineCat的GetName
func (m *MachineCat) GetName() {
fmt.Printf("MachineCat Name: %v\n", m.Name)
}
輸出內容
//MachineCat Name: newMachineCat
如果需要再MachineCat對象調用Animal的方法
machineCat.Animal.GetName()
輸出內容
Animal name: newMachineCat
由此可見Animal是machineCat的一部分,可以直接調用成員的方法。
多態的參數
多態的一個主要應用是傳入的類型為父類對象,在實例化使用的時候,調用的方法由傳入對象的實例決定。
func check(animal IAnimal) {
animal.GetName()
}
check(machineCat)
輸出內容
MachineCat Name: newMachineCat
多重繼承
type Animal struct {
Name string
}
type Machine struct {
MachineName string
}
type MachineCat struct {
*Animal
*Machine
}
//receive類型為Animal的GetName
func (a *Animal) GetName() {
fmt.Printf("Animal name: %v\n", a.Name)
}
//receive類型為Machine的Print
func (m *Machine) Print() {
fmt.Printf("Machine name: %v\n", m.MachineName)
}
func main() {
//實例化machineCat
machineCat := &MachineCat{
&Animal{
Name: "machine animal name",
},
&Machine{
MachineName: "machine name",
},
}
machineCat.GetName()
machineCat.Print()
}
輸出內容
Animal name: machine animal name
Machine name: machine name
可以看到多態繼承和單繼承的區別就是組合中嵌套了更多的匿名struct。可以看到在子類(名義上)中分別繼承了Animal的GetName()方法和Machine的Print()的方法。
如果多重繼承中,父類實現了相同的方法,會發生什么情況呢?
代碼中Machine添加一個GetName()的方法。
//receive類型為Machine的GetName
func (m *Machine) GetName() {
fmt.Printf("Machine name: %v\n", m.MachineName)
}
出現錯誤:
ambiguous selector machineCat.GetName
在多重繼承的父類有相同的方法的時候,就會出現子類出現模糊不清的調用,編譯器無法通過。
總結
- go繼承是通過嵌套匿名struct實現繼承。
- go繼承在本質上還是組合。
- 子類要調用父類的實現可以通過調用組合中的父類對象的方法。
- 多重繼承中不允許多個父類出現相同的方法。