Go语言入门18:面向对象
Table of Contents
Go 语言入门基础学习笔记之 Go 语言的面向对象
面向对象特征
Go 语言中的对象和类的概念,实际上就是一个结构体绑定对应的方法来实现的。
封装
Go 语言的封装概念,事实上就是针对包的封装。
创建一个普通的结构体来定义类:
type Hero struct {
Name string
Ad int
Level int
}
如果类名首字母大写,表示其他包能够访问,是一个公共类。同理类中的成员变量也需要大写来保证外部其他包能够访问,否则只能当前程序能够访问使用。
Go 语言通过首字母大小写来区别是否对外部开放。
如上一节函数与成员方法,给结构体绑定一个成员方法:
func (h *Hero) Show() {
fmt.Println("Name:", h.Name)
fmt.Println("Ad:", h.Ad)
fmt.Println("level:", h.level)
}
func (h *Hero) GetName() string {
return h.Name
}
func (h *Hero) SetName(name string) {
h.Name = name
}
如果传参写成 h Hero
只是调用该对象方法的一个副本,即值传递(拷贝),改变的不是原来变量的值。同时,传参时最好不要写成 this
或者 self
的形式。
func main() {
h := Hero{Name: "Lina", Ad: 100, level: 1}
h.Show()
fmt.Println(h.GetName())
h.SetName("Luna")
fmt.Println(h.GetName())
}
// 输出结果
Name: Lina
Ad: 100
level: 1
Lina
Luna
继承
实际上,Go 语言不存在狭义上的继承,但是在 Go 语言中可以把一个结构体作为另一个结构体的成员变量的类型。即可以把一个结构体类型当成一个基础的数据类型。比如:
定义一个父类和方法:
// 定义父类
type Human struct {
name string
sex string
}
// 定义父类的方法
func (h *Human) Eat() {
fmt.Println("Human Eat()...")
}
func (h *Human) Walk() {
fmt.Println("Human Walk()...")
}
这时父类的 Human
这种结构体类型就可以作为一种基础的数据类型在子类中应用。
// 定义子类
type SuperMan struct {
Human // 继承Human类中的字段和方法
level int
}
在子类中,也可以重写父类的方法或是添加一个新的方法。
// 重写父类的方法
func (s *SuperMan) Eat() {
fmt.Println("SuperMan Eat()...")
}
// 定义子类的新方法
func (s *SuperMan) Fly() {
fmt.Println("SuperMan Fly()...")
}
// 定义子类的新方法
func (s *SuperMan) Print() {
fmt.Println("name:", s.name)
fmt.Println("sex:", s.sex)
fmt.Println("level:", s.level)
}
func main() {
var s SuperMan
s.name = "li4"
s.sex = female
s.level = 88
//也可以 s := SuperMan{Human{"li4", "female"}, 88}
s.Eat() // SuperMan Eat()...
s.Walk() // Human Walk()...
s.Fly() // SuperMan Fly()...
s.Print()
}
// 输出结果
SuperMan Eat()...
Human Walk()...
SuperMan Fly()...
name: li4
sex: female
level: 88
多态
Go 语言实现多态需要利用到接口,实现多态有几个基本的要求:
- 有一个父类(接口)
- 有子类,实现了父类(接口)中的全部接口方法
- 父类(接口)的变量(指针) 指向 (引用) 子类的具体数据变量(接口是一个指针)
首先,需要定义一个接口,接口中包含一些基本的方法:
type AnimalIF interface {
Sleep()
GetColor() string // 获取动物的颜色
GetType() string // 获取动物的种类
}
接着,我们可以定义一个具体的类:
type Cat struct {
color string // 猫的颜色
}
随后,我们需要让这个类实现这个接口,即实现接口全部的方法:
func (c *Cat) Sleep() {
fmt.Println("Cat is Sleep")
}
func (c *Cat) GetColor() string {
return c.color
}
func (c *Cat) GetType() string {
return "Cat"
}
我们还可以定义另外一个具体的类:
type Dog struct {
color string // 狗的颜色
}
同样让这个类实现上面的接口:
func (d *Dog) Sleep() {
fmt.Println("Dog is Sleep")
}
func (d *Dog) GetColor() string {
return d.color
}
func (d *Dog) GetType() string {
return "Dog"
}
由此,两个类都实现了同一个接口,我们可以通过接口来实现多态。
func showAnimal(animal AnimalIF) { // 传递接口类型
animalType := animal.GetType()
animalColor := animal.GetColor()
fmt.Printf("This is a %s, its color is %s\n", animalType, animalColor)
animal.Sleep()
}
func main() {
cat := &Cat{color: "white"}
dog := &Dog{color: "black"}
showAnimal(cat)
showAnimal(dog)
}
// 输出结果
This is a Cat, its color is white
Cat is Sleep
This is a Dog, its color is black
Dog is Sleep
在上面这段代码中,cat
和 dog
实际上已经是一个接口类型的变量,这是因为 Cat{color: "white"}
和 Dog{color: "black"}
都是一个实现了接口中所有方法的结构体,事实上它已经是一个接口了,但是由于结构体是一个值类型,而接口时一个引用类型,因此可以把结构体的地址赋值给一个接口变量(接口是一个指针),于是 cat
和 dog
就是一个接口变量。
如果一个结构体并没有实现接口中的所有方法,那么它就不能是一个接口类型,因此如果进行上面的操作时,就会发生报错,提示“它不是一个接口类型,因为没有实现 Sleep 方法”
参考课程: