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继承在本质上还是组合。
- 子类要调用父类的实现可以通过调用组合中的父类对象的方法。
- 多重继承中不允许多个父类出现相同的方法。