前言
Go语言的面向对象学习笔记
方法
- Go中的方法是作用在指定类型上的,任何自定义类型都可以有自己的方法
- 将函数与结构体绑定,形成逻辑上的方法
值传递
定义方法
- 在调用方法时,结构体变量会作为参数传递给方法,而函数不会
- 对于函数而言,在传递参数时,定义函数时的参数为值类型,调用函数时必须传递值类型;定义函数时的参数为引用类型,调用函数时必须传递引用类型
- 对于方法而言,在传递参数时,无论定义方法时的参数为引用类型还是值类型,调用方法时既可以是值类型,也可以是引用类型,不过实际是值类型还是引用类型由定义方法时的形参决定
1 2 3 4 5 6 7
| type 结构体名 struct { ... }
func (结构体变量名 结构体名) 方法名(形参列表) 返回值类型 { ... }
|
内置类型也可以定义方法
1 2 3 4 5 6 7 8 9 10
| type integer int
func (i integer) print() { fmt.Println(i) }
func main() { var i integer = 1 i.print() }
|
实现String()方法
- 如果一个结构体实现了
String()
方法,那么在fmt.Println()
时会执行这个方法
1 2 3 4 5 6 7 8 9 10
| type integer int
func (结构体变量名 结构体名) String() string { return 结构体变量名.属性名 }
func main() { var 结构体名 = 结构体名{"属性名": 属性值} fmt.Println(&结构体名) }
|
方法的调用
1 2 3 4
| func main() { var 结构体变量名 结构体名 结构体名.方法名(实参列表) }
|
指针传递
方法的定义
- 由于结构体变量的赋值是值传递,所以如果直接传递结构体变量给方法会降低效率,为了提升效率通常将结构体变量指针传递给方法
1 2 3 4 5 6 7
| type 结构体名 struct { ... }
func (结构体变量名 *结构体名) 方法名(形参列表) 返回值类型 { ... }
|
方法的调用
1 2 3 4
| func main() { var 结构体变量名 结构体名 (&结构体名).方法名(实参列表) }
|
简写
1 2 3 4
| func main() { var 结构体变量名 结构体名 结构体名.方法名(实参列表) }
|
封装
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| type persion struct { name string }
func newPersion(name string) *Persion { return &student{ name: name, } }
func (persion *persion) GetName() string { return persion.name }
func (persion *persion) SetName(name string) string { persion.name = name }
|
继承
指定匿名结构体作为继承关系
继承一个结构体
1 2 3 4 5 6 7
| type Father struct { ... }
type Son struct { Father }
|
调用父结构体的属性和方法
- 可以直接使用父结构体的属性和方法
- 先看当前结构体中是否存在指定的属性或方法,如果有就调用当前结构体中的属性或方法
- 如果没有再看匿名结构体中是否存在指定的属性或方法,如果有就调用匿名结构体中的属性或方法
- 如果匿名结构体中仍然不存在指定的属性或方法,就报错
- 被嵌套的结构体无论是首字母大写还是首字母小写,都能够被继承复用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| type Father struct { privateField string PublicField string }
func (father *Father) privateMethod() { ... }
func (father *Father) PublicMethod() { ... }
type Son struct { Father }
func main() { var son = Son{} son.privateField = "" son.PublicField = "" son.privateMethod() son.PublicMethod() }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| type Father struct { privateField string PublicField string }
func (father *Father) privateMethod() { ... }
func (father *Father) PublicMethod() { ... }
type Son struct { Father }
func main() { var son = Son{} son.Father.privateField = "" son.Father.PublicField = "" son.Father.privateMethod() son.Father.PublicMethod() }
|
继承多个结构体
1 2 3 4 5 6 7 8 9 10 11 12
| type Father struct { ... }
type Mother struct { ... }
type Son struct { Father Mother }
|
调用父结构体的属性和方法
- 必须使用父结构体名调用父结构体的属性和方法
- 被嵌套的结构体无论是首字母大写还是首字母小写,都能够被继承复用
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
| type Father struct { privateField string PublicField string }
func (father *Father) privateMethod() { ... }
func (father *Father) PublicMethod() { ... }
type Mother struct { privateField string PublicField string }
func (mother *Mother) privateMethod() { ... }
func (mother *Mother) PublicMethod() { ... }
type Son struct { Father Mother }
func main() { var son = Son{} son.Father.privateField = "" son.Father.PublicField = "" son.Father.privateMethod() son.Father.PublicMethod() son.Mother.privateField = "" son.Mother.PublicField = "" son.Mother.privateMethod() son.Mother.PublicMethod() }
|
直接实例化结构体
实例化值属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| type Father struct { Name string }
type Son struct { Father }
func main() { var son = Son{ Father{ privateField: "" } } }
|
实例化引用属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| type Father struct { Name string }
type Son struct { *Father }
func main() { var son = Son{ &Father{ privateField: "" } } }
|
使用基本类型匿名属性作为父结构体
1 2 3 4 5 6 7 8
| type Son struct { int }
func main() { var son = Son{} son.int = 1 }
|
接口(interface)
- 接口中只能声明没有实现的方法,不能有方法体,接口中不允许定义任何属性
- 接口能实现高内聚低耦合的编程思想
- 实现接口就是指结构体实现了接口所指定的所有抽象方法,必须实现所有接口定义的抽象方法才是实现接口
- Go中接口不需要显式实现,只要结构体实现了接口所指定的所有抽象方法,就完成了对接口的实现
- 接口类型是一个引用类型,如果没有初始化就使用会返回nil
- 接口名推荐以er结尾
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| type 接口名 interface { 抽象方法名(形参列表) 返回值列表 }
type 实现接口的结构体名 struct { ... } func (this 实现接口的结构体名) 抽象方法名() 返回值列表 { ... }
func main() { var 结构体变量名 接口名 = 实现接口的结构体名{} 结构体变量名.抽象方法名() }
|
实现多个接口
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
| type 接口名1 interface { 抽象方法名1(形参列表) } type 接口名2 interface { 抽象方法名2(形参列表) }
type 实现接口的结构体名 struct { ... } func (this 实现接口的结构体名) 抽象方法名1() { ... } func (this 实现接口的结构体名) 抽象方法名2() { ... }
func main() { var 结构体变量名1 接口名1 = 实现接口的结构体名{} 结构体变量名1.抽象方法名1() var 结构体变量名2 接口名2 = 实现接口的结构体名{} 结构体变量名2.抽象方法名2() }
|
实现继承了多接口的接口
- 一个接口继承了多个接口时,不允许出现同名方法,否则会编译报错:重复定义
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
| type 接口名1 interface { 抽象方法名1(形参列表) } type 接口名2 interface { 抽象方法名2(形参列表) } type 接口名3 interface { 接口名1 接口名2 抽象方法名3(形参列表) }
type 实现接口的结构体名 struct { ... } func (this 实现接口的结构体名) 抽象方法名1() { ... } func (this 实现接口的结构体名) 抽象方法名2() { ... } func (this 实现接口的结构体名) 抽象方法名3() { ... }
func main() { var 结构体变量名3 接口名3 = 实现接口的结构体名{} 结构体变量名3.抽象方法名1() 结构体变量名3.抽象方法名2() 结构体变量名3.抽象方法名3() }
|
空接口
- 没有任何方法的接口就是空接口,所以所有类型都实现了空接口
1 2 3
| type 接口名 interface {}
var 变量名 接口名 = 任意类型变量
|
1
| var 变量名 interface{} = 任意类型变量
|
引用类型和值类型结构体的实现
值类型结构体实现接口
1 2 3 4 5 6 7 8 9 10 11
| type 接口名 interface { 抽象方法名(形参列表) }
type 实现接口的结构体名 struct {} func (this 实现接口的结构体名) 抽象方法名() {}
func main() { var 结构体变量名 接口名 = 实现接口的结构体名{} 结构体变量名.抽象方法名() }
|
引用类型结构体实现接口
1 2 3 4 5 6 7 8 9 10 11
| type 接口名 interface { 抽象方法名(形参列表) }
type 实现接口的结构体名 struct {} func (this *实现接口的结构体名) 抽象方法名() {}
func main() { var 结构体变量名 接口名 = &实现接口的结构体名{} 结构体变量名.抽象方法名() }
|
接口体现多态的形式
- 多态参数
- 在定义函数的形参时为接口类型,在调用函数时传递的实参为实现接口的类型
- 多态数组
- 在定义数组时定义数组中存放的数据类型为接口类型,在向数组中添加元素时添加实现了接口的数据类型的数据
类型断言
- 将实现了接口的结构体变量赋值给接口变量时,数据类型就变成了接口,无法继续使用之前的数据类型
- 可以通过类型断言
.(数据类型)
,将接口类型转换为实现接口的结构体类型,如果转换失败就报错
1 2 3 4
| var a interface{} var b int a = b b, ok = a.(int)
|
判断类型断言是否失败
- 如果类型断言失败会panic导致程序崩溃,为了防止程序崩溃可以通过第二个返回值判断断言是否失败,如果成功返回true,如果失败返回false
1 2 3 4 5 6 7
| var a interface{} var b float32 a = b b, ok = a.(float64) if !ok { fmt.Println("convert fail") }
|
完成
参考文献
哔哩哔哩——喔咔咔耶耶
哔哩哔哩——尚硅谷
哔哩哔哩——筱筱知晓