面向对象初识
模拟构造函数
Go和传统的面向对象语言如Java有着很大区别。结构体没有构造函数初始化功能,可以通过以下方式模拟: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29package main
import (
"fmt"
)
type Person struct {
Name string
Age int
}
func NewPersonByName(name string) *Person {
return &Person{
Name: name,
}
}
func NewPersonByAge(age int) *Person {
return &Person{
Age: age,
}
}
func main() {
p := NewPersonByName("zs")
fmt.Println(p) // {zs 0}
}NewPersonByName
和NewPersonByAge
两个不同的函数表示不同的Person
构造过程。
父子关系结构体初始化
Person可以看做父类,Student是子类,子类需要继承父类的成员,该如何处理? 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38package main
import (
"fmt"
)
type Person struct {
Name string
Age int
}
type Student struct {
Person
ClassName string
}
//构造父类
func NewPerson(name string, age int) *Person {
return &Person{
Name: name,
Age: age,
}
}
//构造子类
func NewStudent(classname string) *Student {
p := &Student{}
p.ClassName = classname
return p
}
func main() {
s := NewStudent("一班")
fmt.Println(s) // &{{ 0} 一班}
}
我们发现Student中的Person也被一并实例化了。Go中没有提供构造函数相关的特殊机制,用户根据自己的需求,将参数使用函数传递到结构体构造参数中即可完成构造函数的任务。
Go中的面向对象初识
在Go中,可以给任意类型(除了指针)添加相应方法: 1
2
3
4
5
6
7
8
9
10type Interger int
func (i Interger) Less (j Interger) bool {
return i < j
}
func main() {
var i Interger = 1
fmt.Print(i.Less(5))
}
方法
方法的定义
面向过程的函数书写案例: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22package main
import (
"fmt"
)
type Person struct {
Name string
Age int
}
func run(p *Person, name string) {
p.Name = name
fmt.Printf("%s is runnig...\n", p.Name)
}
func main() {
p1 := &Person{}
run(p1, "zs")
}
在某些情况下,我们要需要声明(定义)方法。比如 Person 结构体:除了有一些字段外( 年龄,姓名..),Person 结构体还有一些行为比如:可以说话、跑步..,通过学习,还可以做算术题,这时就要用方法才能完成。
Golang 中的方法是作用在指定的数据类型上的(即:和指定的数据类型绑定),因此自定义类型,都可以有方法,而不仅仅是 struct。
方法的声明和调用: 1
2
3
4func (recevier type) methodName(参数列表) (返回值列表){
//方法体
return 返回值
}
示例代码: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30package main
import (
"fmt"
)
type Person struct {
Name string
Age int
}
func (p *Person) run() { // p为接收器
fmt.Printf("%s is runnig...\n", p.Name)
}
func main() {
//方式一
p1 := Person{
Name: "Tom",
Age: 18,
}
p1.run()
//方式二
p2 := new(Person)
p2.Name = "Jerry"
p2.run()
}
Go方法本质
Go的方法是一种作用于特定类型变量的函数,这种特定类型的变量叫做接收器(Receiver)。如果特定类型理解为结构体或者“类”时,接收器就类似于其他语言的this或者self。
在Go中,接收器可以是任何类型,不仅仅是结构体。
依此我们看出,Go中的方法和其他语言的方法类似,但是Go语言的接收器强调方法的作用对象是实例。
方法与函数的区别就是:函数没有作用对象。
理解接收器
上述Person案例中,接收器类型是*Person
,属于指针类型,非常接近Java中的this
,由于指针的特性,调用方法时,修改接收器指针的任意长远变量,在方法结束后,修改都是有效的。
当方法作用于非指针接收器时,Go语言会在代码运行时将接收器的值复制一份,在非指针接收器的方法中可以获取接收器的成员值,但修改后无效,如下所示: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29package main
import "fmt"
//定义一个表示点的结构体
type Point struct {
X int
Y int
}
//非指针接收器
func (p Point) Add(otherP Point) Point {
return Point{
p.X + otherP.X,
p.Y + otherP.Y,
}
}
func main() {
p1 := Point{1, 1}
p2 := Point{2, 2}
result := p1.Add(p2)
fmt.Println(result) // {3 3}
}
一般情况下,小对象由于复制时速度较快,适合使用非指针接收器,大对象因为复制性能较低,适合使用指针接收器,此时再接收器和参数之间传递时不进行复制,只传递指针。