通过组合实现类的继承和方法重写
要实现面向对象的编程,就必须实现面向对象编程的三大特性:封装、继承和多态。
1 封装
类的定义及其内部数据的定义可以看作是类的属性,基于类定义的函数方法则是类的成员方法。
2 继承
Go 语言中,没有直接提供继承相关的语法实现,可以通过 组合 的方式间接实现类似的功能,所谓组合,就是将一个类型嵌入到另一个类型,从而构建新的类型结构。
具体实现如下所示:
-
定义一个基础的类
Animal
,其中定义了Name
属性以及Call
、FavorFood
和GetName
成员方法。type Animal struct { Name string } func (a Animal) Call() string { return "动物的叫声..." } func (a Animal) FavorFood() string { return "爱吃的食物..." } func (a Animal) GetName() string { return a.Name }
-
定义一个新的类
Dog
,继承Animal
类的属性和成员方法。type Dog struct { Animal }
-
在
main
方法中对Animal
和Dog
进行调用测试。animal := GoClass.Animal{"中华田园犬"} dog := GoClass.Dog{animal} fmt.Println(dog.GetName()) fmt.Println(dog.Call()) fmt.Println(dog.FavorFood())
3 多态
这里重写方法 FavorFood
和 Call
,如下所示:
func (d Dog) FavorFood() string {
return "骨头"
}
func (d Dog) Call() string {
return "汪汪汪"
}
在 main
方法中进行调用,如下所示:
fmt.Print(dog.Animal.Call())
fmt.Println(dog.Call())
fmt.Print(dog.Animal.FavorFood())
fmt.Println(dog.FavorFood())
4 其他问题
4.1 多继承同名方法冲突处理
需要注意组合的不同类型之间包含同名方法,比如 Animal
和 Pet
都包含了 GetName
方法,如果子类 Dog
没有重写该方法,直接在 Dog
实例上调用的话会报错:
func main() {
animal := Animal{"中华田园犬"}
pet := Pet{"宠物狗"}
dog := Dog{animal, pet}
fmt.Println(dog.GetName())
}
需要显式的指定调用那个父类的方法,修改如下则不报错:
func main() {
animal := Animal{"中华田园犬"}
pet := Pet{"宠物狗"}
dog := Dog{animal, pet}
fmt.Println(dog.GetName())
}
4.2 为组合类型设置别名
能够设置继承父类的名称,示例如下:
type Dog struct {
Animal *GoClass.Animal
Pet *GoClass.Pet
}
animal := GoClass.Animal{"中华田园犬"}
pet := GoClass.Pet{"宠物狗"}
dog := Dog{&animal, &pet}
fmt.Println(dog.Pet.GetName())
fmt.Println(dog.Animal.GetName())