方法
# 方法
# 1.1 什么是方法
Go
语言中同时有函数和方法.一个方法就是一个包含了接受者的函数,接受者可以是命名类型或者结构体类型的一个值或者是一个指针.所有给定类型的方法属于该类型的方法集- 方法只是一个函数,它带有一个特殊的接收器类型,它是在
func
关键字和方法名之间编写的.接收器可以是struct
类型或非struct
类型.接收方可以在方法内部访问. - 方法能给用户自定义的类型添加新的行为.**它和函数的区别在于方法有一个接收者,给一个函数添加一个接收者,那么它就变成了方法.**接收者可以是值接收者,也可以是指针接收者.
- 在调用方法的时候,值类型既可调用值接收者的方法,也可以调用指针接收者的方法;指针类型既可以调用指针接收者的方法,也可以调用值接收者的方法.
- 也就是说,不管方法的接收者时什么类型,该类型的值和指针都可以调用,不必严格符合接收者的类型.
# 1.2 方法的语法
- 定义方法的语法
func (t Type) methodName(parameter list)(return list) {
}
func funcName(parameter list)(return list){
}
1
2
3
4
5
6
2
3
4
5
6
实例代码:
package main
import (
"fmt"
)
type Employee struct {
name string
salary int
currency string
}
/*
displaySalary() method has Employee as the receiver type
*/
func (e Employee) displaySalary() {
fmt.Printf("Salary of %s is %s%d", e.name, e.currency, e.salary)
}
func main() {
emp1 := Employee {
name: "Sam Adolf",
salary: 5000,
currency: "$",
}
emp1.displaySalary() //Calling displaySalary() method of Employee type
}
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
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
# 可以定义相同的方法名
示例代码:
package main
import (
"fmt"
"math"
)
type Rectangle struct {
width, height float64
}
type Circle struct {
radius float64
}
func (r Rectangle) area() float64 {
return r.width * r.height
}
//该 method 属于 Circle 类型对象中的方法
func (c Circle) area() float64 {
return c.radius * c.radius * math.Pi
}
func main() {
r1 := Rectangle{12, 2}
r2 := Rectangle{9, 4}
c1 := Circle{10}
c2 := Circle{25}
fmt.Println("Area of r1 is: ", r1.area())
fmt.Println("Area of r2 is: ", r2.area())
fmt.Println("Area of c1 is: ", c1.area())
fmt.Println("Area of c2 is: ", c2.area())
}
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
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
运行结果
Area of r1 is: 24
Area of r2 is: 36
Area of c1 is: 314.1592653589793
Area of c2 is: 1963.4954084936207
1
2
3
4
2
3
4
- 虽然method的名字一模一样,但是如果接收者不一样,那么method就不一样
- method里面可以访问接收者的字段
- 调用method通过.访问,就像struct里面访问字段一样
# 1.3 方法和函数
- 既然已经有了函数,为什么还要使用方法?
示例代码:
package main
import (
"fmt"
)
type Employee struct {
name string
salary int
currency string
}
/*
displaySalary() method converted to function with Employee as parameter
*/
func displaySalary(e Employee) {
fmt.Printf("Salary of %s is %s%d", e.name, e.currency, e.salary)
}
func main() {
emp1 := Employee{
name: "Sam Adolf",
salary: 5000,
currency: "$",
}
displaySalary(emp1)
}
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
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
在上面的程序中,
displaySalary
方法被转换为一个函数,而Employee struct
作为参数传递给它.这个程序也产生了相同的输出:Salary of Sam Adolf is $5000
.
为什么可以用函数来写相同的程序呢?有以下几个原因:
- 1.
Go
不是一种纯粹面向对象的编程语言,它不支持类.因此,类型的方法是一种实现类似于类的行为的方法. - 2.相同名称的方法可以在不同的类型上定义,而具有相同名称的函数是不允许的.假设有一个正方形和圆形的结构.可以在正方形和圆形上定义一个名为
Area
的方法.这是在下面的程序中完成的.
# 1.4 变量作用域
作用域为已声明标识符所表示的常量、类型、变量、函数或包在源代码中的作用范围.
Go
语言中变量可以在三个地方声明:
- 函数内定义的变量称为局部变量
- 函数外定义的变量称为全局变量
- 函数定义中的变量称为形式参数
# 局部变量
- 在函数体内声明的变量称之为局部变量,它们的作用域只在函数体内,参数和返回值变量也是局部变量.
# 全局变量
- 在函数体外声明的变量称之为全局变量,首字母大写全局变量可以在整个包甚至外部包(被导出后)使用.
package main
import "fmt"
/* 声明全局变量 */
var g int
func main() {
/* 声明局部变量 */
var a, b int
/* 初始化参数 */
a = 10
b = 20
g = a + b
fmt.Printf("结果: a = %d, b = %d and g = %d\n", a, b, g)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
结果
结果: a = 10, b = 20 and g = 30
1
# 形式参数
- 形式参数会作为函数的局部变量来使用
# 指针作为接收者
- 若不是以指针作为接收者,实际只是获取了一个
copy
,而不能真正改变接收者的中的数据
func (b *Box) SetColor(c Color) {
b.color = c
}
1
2
3
2
3
示例代码
package main
import (
"fmt"
)
type Rectangle struct {
width, height int
}
func (r *Rectangle) setVal() {
r.height = 20
}
func main() {
p := Rectangle{1, 2}
s := p
p.setVal()
fmt.Println(p.height, s.height)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
结果
20 2
1
如果没有那个*
,则值就是2 2
# 1.5 method继承
method
是可以继承的,如果匿名字段实现了一个method
,那么包含这个匿名字段的struct
也能调用该method
package main
import "fmt"
type Human struct {
name string
age int
phone string
}
type Student struct {
Human //匿名字段
school string
}
type Employee struct {
Human //匿名字段
company string
}
func (h *Human) SayHi() {
fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
}
func main() {
mark := Student{Human{"Mark", 25, "222-222-YYYY"}, "MIT"}
sam := Employee{Human{"Sam", 45, "111-888-XXXX"}, "Golang Inc"}
mark.SayHi()
sam.SayHi()
}
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
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
运行结果:
Hi, I am Mark you can call me on 222-222-YYYY
Hi, I am Sam you can call me on 111-888-XXXX
1
2
2
package main
import "fmt"
func main() {
/*
OOP中的继承性:
如果两个类(class)存在继承关系,其中一个是子类,另一个作为父类,那么:
1.子类可以直接访问父类的属性和方法
2.子类可以新增自己的属性和方法
3.子类可以重写父类的方法(orverride,就是将父类已有的方法,重新实现)
Go语言的结构体嵌套:
1.模拟继承性: is - a
type A struct {
field
}
type B struct {
A //匿名字段
}
2.模拟聚合关系: has - a
type C struct {
field
}
type D struct {
c C //聚合关系
}
*/
//1.创建Person类型
p1 := Person{name: "王二狗", age: 30}
fmt.Println(p1.name, p1.age) //父类对象,访问父类的字段属性
p1.eat() //父类对象,访问父类的方法
//2.创建Student类型
s1 := Student{Person{"Ruby", 18}, "Golang学习"}
fmt.Println(s1.name) //s2.Person.name
fmt.Println(s1.age) //子类对象,可以直接访问父类的字段(其实就是提升字段)
fmt.Println(s1.school) //子类对象,访问自己新增的字段属性
s1.eat() //子类对象,访问父类的方法
s1.study() //子类对象,访问自己新增的方法
s1.eat() //如果存在方法的重写,子类对象访问重写的方法
}
//1.定义一个"父类"
type Person struct {
name string
age int
}
//2.定义一个"子类"
type Student struct {
Person //结构体嵌套,模拟继承性
school string
}
//3.方法
func (p Person) eat() {
fmt.Println("父类的方法,吃海鲜......")
}
//新增方法
func (s Student) study() {
fmt.Println("子类新增方法,学生们学习...")
}
func (s Student) eat() {
fmt.Println("子类重写的方法: 吃香喝辣...")
}
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
打印输出
$ go run 38.struct/demo01-struct.go
王二狗 30
父类的方法,吃海鲜......
Ruby
18
Golang学习
子类重写的方法: 吃香喝辣...
子类新增方法,学生们学习...
子类重写的方法: 吃香喝辣...
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 1.6 method重写
package main
import "fmt"
type Human struct {
name string
age int
phone string
}
type Student struct {
Human //匿名字段
school string
}
type Employee struct {
Human //匿名字段
company string
}
//Human定义method
func (h *Human) SayHi() {
fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
}
//Employee的method重写Human的method
func (e *Employee) SayHi() {
fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name,
e.company, e.phone) //Yes you can split into 2 lines here.
}
func main() {
mark := Student{Human{"Mark", 25, "222-222-YYYY"}, "MIT"}
sam := Employee{Human{"Sam", 45, "111-888-XXXX"}, "Golang Inc"}
mark.SayHi()
sam.SayHi()
}
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
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
运行结果:
Hi, I am Mark you can call me on 222-222-YYYY
Hi, I am Sam, I work at Golang Inc. Call me on 111-888-XXXX
1
2
2
- 方法是可以继承和重写的
- 存在继承关系时,按照就近原则,进行调用
上次更新: 2024/04/09, 16:48:42
- 01
- AWS NAT-NetWork-Firwalld配置(一)04-09
- 02
- AWS NAT-NetWork-Firwalld配置(二)04-09
- 03
- kubernetes部署minio对象存储01-18