
Go语言基础(面向对象)
一、面向对象程序设计思想
\quad
Go
语言支持面向对象程序设计思想。面向对象(Object Oriented
, OO
)就是将要研究的事物抽象成对象,然后对对象的状态和行为进行定义。
1.1 对象
\quad
在Go
语言里,对象(Object
)可以是最简单的内置数据类型,或者是复杂的结构体。对象不仅能表示具体的事物,还能表示抽象的规则、计划或事件。
1.2 对象的状态
\quad 每一个对象都具有状态,对象通常用数值来描述它的状态。
1.3 对象的操作
\quad
对象还应有操作,用于改变对象的状态,操作就是对象的行为。在Go
语言中,对对象的操作称为Method
(方法),Method
就是在函数前增加了一个接收者(Receiver
)对象。这样,操作就和对象真正关联起来了。
1.4 面向对象编程的好处
- 封装
- 继承
- 多态
二、Method 方法
2.1 Method 的基本定义
Go
语言的Method
类似于一个函数,只是函数名前多了个绑定类型参数——receiver
。Method
中的Receiver
可以是内置类型、自定义类型、结构体或指针类型。Method
的基本定义格式如下:func (recv receiver_type)funcName(param1 param1Type, ...)(return1 returnType1, ...){ ...... }
Method
的基本定义案例:type Rectangle struct { // 矩形对象 width int height int } func (rectangle Rectangle) area() int { // 函数area()是Rectangle对象的一个方法 return rectangle.width * rectangle.height // 计算矩形面积 } func main() { rect := Rectangle{3, 4} fmt.Println(rect.area()) // 12 }
2.2 多个 Method 可以同名
- 在定义
Method
时,多个Method
可以同名,但如果接收者不一样,那Method
就不一样。 - 如果普通类型作为
Receiver
,它只是一个值传递;而指针类型作为Receiver
,它将是一个引用传递。两者的差别在于,指针作为Receiver
会对实例对象的内容发生操作,而普通类型作为Receiver
仅是以副本作为操作对象,并不对原实例对象发生操作。 Method
的名字可能一样,但如果接收者不一样,那Method
就不一样。Method
里面可以访问接收者的字段,使用点号“.
”操作符访问。
- 案例:
type Rectangle struct { // 矩形对象 width int height int } type Circle struct { // 圆形对象 radius float32 } func (rectangle Rectangle) area() int { // 函数area()是rectangle对象的一个方法 return rectangle.width * rectangle.height // 计算矩形面积 } func (circle Circle) area() float32 { // 函数area()是circle对象的一个方法 return circle.radius * circle.radius * math.Pi // 计算圆形面积 } func main() { rectangle := Rectangle{3, 4} circle := Circle{5} fmt.Println(rectangle.area()) // 12 fmt.Println(circle.area()) // 78.53982 }
2.3 指针作为 Receiver
\quad
如果普通类型作为Receiver
,它只是一个值传递;而指针类型作为Receiver
,它将是一个引用传递。两者的差别在于,指针作为Receiver
会对实例对象的内容发生操作,而普通类型作为Receiver
仅是以副本作为操作对象,并不对原实例对象发生操作。
\quad
和其他面向对象语言相比,在Go
语言中没有隐藏的this
指针。而在C++
、Java
、C#
这类语言中,它们的成员方法中都带有一个隐藏的this
指针,而在Python
语言中会隐藏一个self
参数,它和其他语言的this
作用一样。
- 案例:定义一个结构体对象
coordinate
,它用于记录平面坐标值x
、y
,方法swap()
的作用是将坐标值x
、y
对换。首先使用普通类型作为Receiver
,观察程序运行结果;再使用指针类型作为Receiver
,观察程序运行结果。type Coordiante struct { x int y int } func (coordiante Coordiante) swap() { coordiante.x, coordiante.y = coordiante.y, coordiante.x // 值传递:普通类型作为Receiver,在方法swap()中对对象属性值的操作并不会改变原实例的值 fmt.Println(coordiante.x, coordiante.y) // 8 6 } func (coordiante *Coordiante) swap2() { coordiante.x, coordiante.y = coordiante.y, coordiante.x // 引用传递:指针类型作为Receiver,在方法swap()中对对象属性值的操作会直接影响到原实例的值 fmt.Println(coordiante.x, coordiante.y) // 8 6 } func main() { coordiante := Coordiante{6, 8} coordiante.swap() fmt.Println("值传递:", coordiante.x, coordiante.y) // 值传递: 6 8 (&coordiante).swap2() fmt.Println("引用传递:", coordiante.x, coordiante.y) // 引用传递: 8 6 }
2.4 匿名 Receiver
- 如果方法代码中不适用
Receiver
参数,那么就可以省略Receiver
的变量名,此时的接收者将是一个匿名Receiver
。示例如下:type Object struct { id int name string } func (Object) msgbox() { fmt.Println("This is a object!") } func (*Object) msgBox() { fmt.Println("This is a object!") } func main() { obj := Object{} ptr := &obj obj.msgbox() ptr.msgBox() }
2.5 Method 的继承
\quad
通过前面的学习,我们了解到通过匿名字段可以实现字段继承,在本节中还会发现Go
语言的一个神奇之处,即Method
也是可以继承的。如果匿名字段实现了一个Method
,那么包含这个匿名字段的Struct
对象也能调用该Method
。
- 案例:结构体
People
作为结构体Teacher
和Student
的匿名字段,sayHi()
是为People
实现的一个方法。作为外层结构,Teacher
和Student
同时也继承了People
所拥有的方法,所以在Teacher
和Student
的对象中可以直接调用方法sayHi()
。
输出:type People struct { name string phone string } type Teacher struct { People department string } type Student struct { People school string } func (people People) sayHi() { fmt.Printf("Hi, I'm %s, you can call me on %s.\n", people.name, people.phone) } func main() { teacher := Teacher{People{"张三", "010-11001"}, "Computer Science"} teacher.sayHi() student := Student{People{"李四", "010-22002"}, "Tsinghua University"} student.sayHi() }
Hi, I'm 张三, you can call me on 010-11001. Hi, I'm 李四, you can call me on 010-22002.
2.6 Method 的重写
\quad
在上例中,如果Student
想要实现自己的sayHi()
方法,可以采用Method
重写的方法来解决。这和匿名字段重名是一样的道理,即外部方法会隐藏匿名字段同名方法。所以可以在Student
上再定义一个Method
,重写了匿名字段的方法。示例如下:
type People struct {
name string
phone string
}
type Teacher struct {
People
department string
}
type Student struct {
People
school string
}
func (people People) sayHi() {
fmt.Printf("Hi, I'm %s, you can call me on %s.\n", people.name, people.phone)
}
func (student Student) sayHi() {
fmt.Printf("Hi, I'm %s, I study in %s, call me on %s.\n", student.name, student.school, student.phone)
}
func main() {
teacher := Teacher{People{"张三", "010-11001"}, "Computer Science"}
teacher.sayHi()
student := Student{People{"李四", "010-22002"}, "Tsinghua University"}
student.sayHi()
}
输出:
Hi, I'm 张三, you can call me on 010-11001.
Hi, I'm 李四, I study in Tsinghua University, call me on 010-22002.