Golang面向对象编程-struct(结构体)
作者:尹正杰
版权声明:原创作品,谢绝转载!否则将追究法律责任。
一.什么是面向对象编程
面向对象编程(Object Oriented Programming,OOP,面向对象程序设计)是一种计算机编程架构。OOP 的一条基本原则是计算机程序是由单个能够起到子程序作用的单元或对象组合而成。OOP 达到了软件工程的三个主要目标:重用性、灵活性和扩展性。为了实现整体运算,每个对象都能够接收信息、处理数据和向其它对象发送信息。
二.面向对象编程常用名词介绍
面向对象程序设计中的概念主要包括:对象、类、数据抽象、继承、动态绑定、数据封装、多态性、消息传递。通过这些概念面向对象的思想得到了具体的体现。
1>.对象(Object)
可以对其做事情的一些东西。一个对象有状态、行为和标识三种属性。
2>.类(class)
一个共享相同结构和行为的对象的集合。类(Class)定义了一件事物的抽象特点。通常来说,类定义了事物的属性和它可以做到的(它的行为)。举例来说,“狗”这个类会包含狗的一切基础特征,例如它的孕育、毛皮颜色和吠叫的能力。类可以为程序提供模版和结构。一个类的方法和属性被称为“成员”。
3>.封装(encapsulation):
第一层意思:将数据和操作捆绑在一起,创造出一个新的类型的过程。第二层意思:将接口与实现分离的过程。
4>.继承
类之间的关系,在这种关系中,一个类共享了一个或多个其他类定义的结构和行为。继承描述了类之间的“是一种”关系。子类可以对基类的行为进行扩展、覆盖、重定义。
5>.组合
既是类之间的关系也是对象之间的关系。在这种关系中一个对象或者类包含了其他的对象和类。组合描述了“有”关系。
6>.多态
类型理论中的一个概念,一个名称可以表示很多不同类的对象,这些类和一个共同超类有关。因此,这个名称表示的任何对象可以以不同的方式响应一些共同的操作集合。
7>.动态绑定
也称动态类型,指的是一个对象或者表达式的类型直到运行时才确定。通常由编译器插入特殊代码来实现。与之对立的是静态类型。
8>.静态绑定
也称静态类型,指的是一个对象或者表达式的类型在编译时确定。
9>.消息传递
指的是一个对象调用了另一个对象的方法(或者称为成员函数)。
10>.方法
也称为成员函数,是指对象上的操作,作为类声明的一部分来定义。方法定义了可以对一个对象执行那些操作。
三.Golang中的对象
在Go语言中,也和 C 或者Python等其他语言一样,我们可以声明(自定义)新的类型,作为其它类型的属性或字段的容器。在Python或是C中,我们称这种数据类型为class类型,而在golang中它也有一个中文名字,叫做结构体。
1.定义一个新的类型
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 10 type Chairman struct { //其中“type...struct”表示定义数据类型的关键字,而Chairman则是我们定义类型的名字。 11 Name string //这里面的所有元素都是Chairman的属性,Name表示我们定义的名字的意思。 12 age int //这个顾名思义就知道是年龄喽,只不过我这里故意将首字母小写,但是你可别小瞧这个小写哟,以后我们在说它的功能。 13 }
2.声明以及定义的类型
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 10 import "fmt" 11 12 type Chairman struct { //其中“type...struct”表示定义数据类型的关键字,而Chairman则是我们定义类型的名字。 13 Name string //这里面的所有元素都是Chairman的属性,Name表示我们定义的名字的意思。 14 age int //这个顾名思义就知道是年龄喽,只不过我这里故意将首字母小写,但是你可别小瞧这个小写哟,以后我们在说它的功能。 15 } 16 17 func main() { 18 var Leader Chairman //声明结构体为自定义的Chairman; 19 var DeputyCadres_1 *Chairman //声明结构体为自定义的Chairman指针; 20 DeputyCadres_2 := new(Chairman) //用内置函数new进行了初始化&{ }; 21 fmt.Println(Leader) //默认初始化为零值; 22 fmt.Println(DeputyCadres_1) //<nil> 未初始化; 23 fmt.Println(DeputyCadres_2) 24 fmt.Println(DeputyCadres_1 == nil) 25 DeputyCadres_1 = new(Chairman) //这种先声明再初始化的方式和直接初始化效果相同。 26 fmt.Println(DeputyCadres_1) 27 fmt.Println(DeputyCadres_1 == nil) 28 } 29 30 31 32 #以上代码执行结果如下: 33 { 0} 34 <nil> 35 &{ 0} 36 true 37 &{ 0} 38 false
3.初始化定义的类型
a>.先声明再赋值
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 10 import "fmt" 11 12 type Chairman struct { //其中“type...struct”表示定义数据类型的关键字,而Chairman则是我们定义类型的名字。 13 Name string //这里面的所有元素都是Chairman的属性,Name表示我们定义的名字的意思。 14 age int //这个顾名思义就知道是年龄喽,只不过我这里故意将首字母小写,但是你可别小瞧这个小写哟,以后我们在说它的功能。 15 } 16 17 func main() { 18 var p Chairman 19 p.Name = "习大大" 20 p.age = 64 21 fmt.Println(p) 22 } 23 24 25 26 #以上代码执行结果如下: 27 {习大大 64}
b>.按照位置初始化
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 10 import "fmt" 11 12 type Chairman struct { //其中“type...struct”表示定义数据类型的关键字,而Chairman则是我们定义类型的名字。 13 Name string //这里面的所有元素都是Chairman的属性,Name表示我们定义的名字的意思。 14 age int //这个顾名思义就知道是年龄喽,只不过我这里故意将首字母小写,但是你可别小瞧这个小写哟,以后我们在说它的功能。 15 } 16 17 func main() { 18 var p Chairman 19 p = Chairman{ 20 "习大大", 21 64, 22 } 23 fmt.Println(p) 24 } 25 26 27 28 #以上代码执行结果如下: 29 {习大大 64}
c>.按照key和value的方式初始化
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 10 import "fmt" 11 12 type Chairman struct { //其中“type...struct”表示定义数据类型的关键字,而Chairman则是我们定义类型的名字。 13 Name string //这里面的所有元素都是Chairman的属性,Name表示我们定义的名字的意思。 14 age int //这个顾名思义就知道是年龄喽,只不过我这里故意将首字母小写,但是你可别小瞧这个小写哟,以后我们在说它的功能。 15 } 16 17 func main() { 18 var p Chairman 19 p = Chairman{ 20 age:124, 21 Name:"毛爷爷", 22 } 23 fmt.Println(p) 24 } 25 26 27 28 #以上代码执行结果如下: 29 {毛爷爷 124}
4.自定义struct案例及其用法案例展示
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 import "fmt" 10 11 type Student struct { // 声明一个新的类型叫Student 12 name string 13 age int 14 } 15 16 17 func AgeConstrast(p1, p2 Student) (Student, int) { // 我们将定义的struct类型传递进来进行比较两个人的年龄,返回年龄大的那个人,并且返回年龄差 18 if p1.age>p2.age { // 比较 p1 和p2这两个人的年龄 19 return p1, p1.age-p2.age 20 } 21 return p2, p2.age-p1.age 22 } 23 func main() { 24 var yinzhengjie Student 25 26 yinzhengjie.name, yinzhengjie.age = "尹正杰", 18 // 赋值初始化 27 28 bingan := Student{age:25, name:"饼干"} //按照key:value方式的初始化赋值 29 30 Leader:= Student{"习大大", 43} // 按照 struct定义顺序初始化值 31 yzj_bg, tb_diff := AgeConstrast(yinzhengjie, bingan) //将我们赋值好的数据传入我们定义的函数中,然后用两个参数接受返回值。 32 yzj_xdd, tp_diff := AgeConstrast(yinzhengjie, Leader) 33 bg_xdd, bp_diff := AgeConstrast(bingan, Leader) 34 fmt.Printf("当【·\033[31;1m%s\033[0m·】 和 【%s】 在一起时, 【%s】 比他大 【%d】 岁!\n", 35 yinzhengjie.name, bingan.name, yzj_bg.name, tb_diff) 36 fmt.Printf("当【·\033[31;1m%s\033[0m·】 和 【%s】 在一起时, 【%s】 比他大 【%d】 岁!\n", 37 yinzhengjie.name, Leader.name, yzj_xdd.name, tp_diff) 38 fmt.Printf("当【·\033[31;1m%s\033[0m·】 和 【%s】 在一起时, 【%s】 比他大 【%d】 岁!\n", 39 bingan.name, Leader.name, bg_xdd.name, bp_diff) 40 } 41 42 43 44 #以上代码执行结果如下: 45 当【·尹正杰·】 和 【饼干】 在一起时, 【饼干】 比他大 【7】 岁! 46 当【·尹正杰·】 和 【习大大】 在一起时, 【习大大】 比他大 【25】 岁! 47 当【·饼干·】 和 【习大大】 在一起时, 【习大大】 比他大 【18】 岁!
5.自定义struct案例及其用法案例展示
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 10 import ( 11 "math" 12 "fmt" 13 ) 14 15 type Point struct { //定义一个结构题体,你可以理解是是Python中的class 16 X,Y float64 17 } 18 19 func (p Point)Distence(q Point) float64 { //给p对象定义一个Distence的方法,你可以理解绑定了一个Distence的方法。 20 return math.Hypot(q.X-p.X,q.Y-p.Y) 21 } 22 23 func main() { 24 p := Point{1,2} 25 q := Point{4,6} 26 fmt.Println((p.Distence(q))) //类的调用方式,注意,如果定义就要如何调用!(这里是调用p的Distence方法。) 27 } 28 29 30 31 #以上代码输出结果如下: 32 5
6.花式玩法struct案例展示(纯中文编程)
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 10 import "fmt" 11 12 type 车的属性 struct { 13 名称 string 14 描述 string 15 是否需要备用 bool 16 } 17 18 type 车的特性 []车的属性 //“车的特性”类型是包含结构体“车的属性”类型的数组切片,你可以理解是起了一个别名。 19 20 func (零件 车的特性) 备件方法()(车的信息 车的特性) { //给“车的特性”绑定一个叫“备件”的方法起名为“零件”。 21 for _,part := range 零件{ 22 if part.是否需要备用 { //只将有备胎的车追加到“车的信息”这个空切片中。 23 车的信息 = append(车的信息,part) 24 } 25 } 26 return 车的信息 27 } 28 29 type 汽车 struct { //“汽车”由“车身大小”组成 30 车身大小 string 31 车的特性 //没有给“车的特性”指定一个名称,我们是要保证实现“内嵌”。这样可以提供自动的委托,不需特殊的声明, 32 // 例如“汽车.备件方法()”和“汽车.车的特性.备件方法()”是等同的。 33 } 34 35 36 var ( 37 特斯拉 = 车的特性{ 38 {"Tesla_90D(加速时间)", "100km/2.9s", true}, 39 {"车身大小", "109.47万元", false}, 40 {"颜色", "red", false}, 41 } 42 43 宝马 = 车的特性{ 44 {"BMW M4敞篷轿跑车(加速时间)", "100km/4.4s", true}, 45 {"价格", "1,098,000美元", true}, 46 {"倍耐力轮胎", "兰博基尼Huracan LP580-2前轮原配", true}, 47 {"夏季冰丝汽车坐垫", "1088.00", true}, 48 } 49 50 兰博基尼 = 车的特性{ 51 {"Avetador(加速时间)", "100km/2.8s", true}, 52 {"价格", "648.80-801.15万", true}, 53 {"颜色", "黑色", false}, 54 {"夏季冰丝汽车坐垫", "1088.00", true}, 55 } 56 ) 57 58 59 60 func main() { 61 roadBike := 汽车{车身大小: "5037×2070×mm", 车的特性: 特斯拉} 62 mountainBike := 汽车{车身大小: "1678*1870*1398", 车的特性: 宝马} 63 recumbentBike := 汽车{车身大小: "4780*2030*1136", 车的特性: 兰博基尼} 64 fmt.Println(roadBike.备件方法()) 65 fmt.Println(mountainBike.备件方法()) 66 fmt.Println(recumbentBike.备件方法()) 67 comboParts := 车的特性{} 68 comboParts = append(comboParts, mountainBike.车的特性...) 69 comboParts = append(comboParts, roadBike.车的特性...) 70 comboParts = append(comboParts, recumbentBike.车的特性...) 71 72 fmt.Println(len(comboParts), comboParts[9:]) 73 fmt.Println(comboParts.备件方法()) 74 } 75 76 77 #以上代码执行结果如下: 78 [{Tesla_90D(加速时间) 100km/2.9s true}] 79 [{BMW M4敞篷轿跑车(加速时间) 100km/4.4s true} {价格 1,098,000美元 true} {倍耐力轮胎 兰博基尼Huracan LP580-2前轮原配 true} {夏季冰丝汽车坐垫 1088.00 true}] 80 [{Avetador(加速时间) 100km/2.8s true} {价格 648.80-801.15万 true} {夏季冰丝汽车坐垫 1088.00 true}] 81 11 [{颜色 黑色 false} {夏季冰丝汽车坐垫 1088.00 true}] 82 [{BMW M4敞篷轿跑车(加速时间) 100km/4.4s true} {价格 1,098,000美元 true} {倍耐力轮胎 兰博基尼Huracan LP580-2前轮原配 true} {夏季冰丝汽车坐垫 1088.00 true} {Tesla_90D(加速时间) 100km/2.9s true} {Avetador(加速时间) 100km/2.8s true} {价格 648.80-801.15万 true} {夏季冰丝汽车坐垫 1088.00 true}]
四.Golang中的匿名字段
结构体里的字段可以只有类型名,而没有字段名,这种字段称为匿名字段。匿名字段可以是一个结构体、切片等复合类型,也可以是 int 这样的简单类型。但建议不要把简单类型作为匿名字段。golang中每种类型只能有一个匿名域。可以用来实现oop中的继承。
1.匿名字段的初始化操作
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 import "fmt" 10 type Student struct { 11 name string //第一个参数“name”叫做字段名称,第二个参数“string”叫做字段类型。 12 age int 13 speciality string 14 int //我们知道“int”是内置的数据类型,我们可以在前面加一个字段名称,如果不加就是用内置类型作为匿名字段。 15 } 16 type Classroom struct { 17 Student // 这是我们自定义的结构体,我们可以在其前面加一个字段名称,如果不加的话就是匿名字段,那么默认 Classroom就包含了Student 的所有字段。 18 area int 19 name string 20 } 21 func main() { 22 yinzhengjie := Classroom{ //我们给“Classroom”这个结构体定义了三个属性,一个是“Student”匿名字段,一个是“area”,还有一个是“name”。以下是赋值操作。 23 Student{ 24 name:"尹正杰", 25 age:18, 26 speciality:"Basketball", 27 int:175, 28 }, 29 100 , 30 "Golang进阶之路", 31 } 32 fmt.Println(yinzhengjie) 33 } 34 35 36 37 38 #以上代码执行结果如下: 39 {{尹正杰 18 Basketball 175} 100 Golang进阶之路}
2.匿名字段的查询操作
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 import "fmt" 10 type Student struct { 11 Name string //第一个参数“name”叫做字段名称,第二个参数“string”叫做字段类型。 12 Age int 13 Speciality string 14 int //我们知道“int”是内置的数据类型,我们可以在前面加一个字段名称,如果不加就是用内置类型作为匿名字段。 15 } 16 type Classroom struct { 17 Student // 这是我们自定义的结构体,我们可以在其前面加一个字段名称,如果不加的话就是匿名字段,那么默认 Classroom就包含了Student 的所有字段。 18 area int 19 name string 20 } 21 22 func MyEcho(p Classroom) { //该函数用来定义查看结构体信息的。 23 fmt.Printf("学生的姓名是:【%v】\n",p.Student.Name) 24 fmt.Printf("学生的身高是:【%d】\n",p.Student.int) 25 fmt.Printf("学生的爱好是:【%s】\n",p.Student.Speciality) 26 fmt.Printf("学生的身高是:【%d】\n",p.Student.int) 27 fmt.Printf("教室的面积是:【%v】\n",p.area) 28 fmt.Printf("教室的名称是:【%s】\n",p.name) 29 30 } 31 func main() { 32 yinzhengjie := Classroom{ //我们给“Classroom”这个结构体定义了三个属性,一个是“Student”匿名字段,一个是“area”,还有一个是“name”。以下是赋值操作。 33 Student{ 34 Name:"尹正杰", 35 Age:18, 36 Speciality:"Basketball", 37 int:175, 38 }, 39 130 , 40 "Golang进阶之路", 41 } 42 MyEcho(yinzhengjie) 43 } 44 45 46 47 #以上代码执行结果如下: 48 学生的姓名是:【尹正杰】 49 学生的身高是:【175】 50 学生的爱好是:【Basketball】 51 学生的身高是:【175】 52 教室的面积是:【130】 53 教室的名称是:【Golang进阶之路】
3.匿名字段的修改
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 import "fmt" 10 type Student struct { 11 Name string //第一个参数“name”叫做字段名称,第二个参数“string”叫做字段类型。 12 Age int 13 Speciality string 14 int //我们知道“int”是内置的数据类型,我们可以在前面加一个字段名称,如果不加就是用内置类型作为匿名字段。 15 } 16 type Classroom struct { 17 Student // 这是我们自定义的结构体,我们可以在其前面加一个字段名称,如果不加的话就是匿名字段,那么默认 Classroom就包含了Student 的所有字段。 18 area int 19 name string 20 } 21 22 func MyEcho(p Classroom) { //该函数用来定义查看结构体信息的。 23 fmt.Printf("学生的姓名是:【%v】\n",p.Student.Name) 24 fmt.Printf("学生的身高是:【%d】\n",p.Student.int) 25 fmt.Printf("学生的爱好是:【%s】\n",p.Student.Speciality) 26 fmt.Printf("学生的身高是:【%d】\n",p.Student.int) 27 fmt.Printf("教室的面积是:【%v】\n",p.area) 28 fmt.Printf("教室的名称是:【%s】\n",p.name) 29 30 } 31 func main() { 32 yinzhengjie := Classroom{ //我们给“Classroom”这个结构体定义了三个属性,一个是“Student”匿名字段,一个是“area”,还有一个是“name”。以下是赋值操作。 33 Student{ 34 Name:"尹正杰", 35 Age:18, 36 Speciality:"Basketball", 37 int:175, 38 }, 39 130 , 40 "Golang进阶之路", 41 } 42 yinzhengjie.Student.Name = "yinzhengjie" //此处我们修改了“Classroom”的匿名字段“Student”的“Name”属性。 43 yinzhengjie.name = "Golang自动化运维工具" //这里我们修改了“Classroom”的“name”属性。 44 MyEcho(yinzhengjie) 45 } 46 47 48 49 #以上代码执行结果如下: 50 学生的姓名是:【yinzhengjie】 51 学生的身高是:【175】 52 学生的爱好是:【Basketball】 53 学生的身高是:【175】 54 教室的面积是:【130】 55 教室的名称是:【Golang自动化运维工具】
五.面向对象之method(方法)
好了,现在我们已经学会了struct的基本知识结合之前学习函数的知识。现在假设有这么一个场景,你定义了一个 struct 叫做长方形,你现在想要计算他的面积,那么按照我们以往的的思路应该会用下面的方式来实现:
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 10 import "fmt" 11 12 const PI = 3.14 //定义pi的大小。 13 14 type Square struct { //定义一个长方形的类型 15 length,width float64 16 } 17 type Trapezoid struct { //定义一个梯形 18 UpperLength float64 19 BelowLength float64 20 Height float64 21 waist float64 22 } 23 type Circle struct { 24 radius float64 25 } 26 27 func SquareArea(a Square) float64 { 28 return a.length * a.width 29 } 30 31 func TrapezoidArea(a Trapezoid)float64 { 32 return (a.UpperLength + a.BelowLength)*a.Height/2 33 } 34 35 func CircleArea(a Circle) float64 { 36 return PI*a.radius*a.radius 37 } 38 39 func SquarePerimeter(a Square)float64 { 40 return (a.width + a.length)*2 41 } 42 43 func TrapezoidPerimeter (a Trapezoid)float64 { 44 return a.UpperLength + a.BelowLength + a.waist*2 45 } 46 47 func CirclePerimeter(a Circle) float64 { 48 return 2*PI*a.radius 49 } 50 51 52 func main() { 53 a := Square{10,20} 54 fmt.Printf("正方形的面积是【%v】\n",SquareArea(a)) 55 fmt.Printf("正方形的周长是【%v】\n",SquarePerimeter(a)) 56 b := Trapezoid{10,20,5,15} 57 fmt.Printf("梯形的面积是【%v】\n",TrapezoidArea(b)) 58 fmt.Printf("梯形的周长是【%v】\n",TrapezoidPerimeter(b)) 59 c := Circle{5} 60 fmt.Printf("圆形的面积是【%v】\n",CircleArea(c)) 61 fmt.Printf("圆形的周长是【%v】\n",CirclePerimeter(c)) 62 }
1.初探method(方法)
以上的方法的确可以实现我们想要的功能,但是每实现一个功能都得新写一个函数,需要重新给它起个名字。那如果让我们求五边形,六边形,多边形等等,就得写多个函数去实现。但是仔细看下以上的代码,其实就是实现了求面积和求周长的功能,如果我们可以将面积和周长分一个类,返回把正方形和梯形等求面积的都同意用一个Area方法去接受岂不更好,也不需要我们没次实现一个功能都得去写一个函数名。今天我们就讲解一下函数的另一种形态,带有接收者的函数,我们称为 method。因此,我们可以把上面的代码修改后一下
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 10 import "fmt" 11 12 const PI = 3.14 //定义pi的大小。 13 14 type Square struct { //定义一个长方形的类型 15 length,width float64 16 } 17 type Trapezoid struct { //定义一个梯形 18 UpperLength float64 19 BelowLength float64 20 Height float64 21 waist float64 22 } 23 type Circle struct { 24 radius float64 25 } 26 27 func (a Square)Area()float64 { //我们可以给正方形设置求面积的(method)方法。 28 return a.width * a.length 29 } 30 func (a Trapezoid) Area() float64 { //我们可以给梯形设置求面积的方法。 31 return (a.UpperLength + a.BelowLength)*a.Height/2 32 } 33 34 func (a Circle) Area() float64{ //我们圆形给正方形设置求面积的方法。 35 return PI*a.radius*a.radius 36 } 37 38 func (a Square)Perimeter()float64 { //我们可以给正方形设置求周长的方法。 39 return (a.width + a.length)*2 40 } 41 42 func (a Trapezoid)Perimeter()float64 { 43 return a.UpperLength + a.BelowLength + a.waist*2 44 } 45 46 func (a Circle)Perimeter()float64 { 47 return 2*PI*a.radius 48 } 49 50 func main() { 51 a := Square{10,20} 52 fmt.Printf("正方形的面积是【%v】\n",a.Area()) //注意其调用方式。 53 fmt.Printf("正方形的周长是【%v】\n",a.Perimeter()) 54 b := Trapezoid{10,20,5,15} 55 fmt.Printf("梯形的面积是【%v】\n",b.Area()) 56 fmt.Printf("梯形的周长是【%v】\n",b.Perimeter()) 57 c := Circle{5} 58 fmt.Printf("圆形的面积是【%v】\n",c.Area()) 59 fmt.Printf("圆形的周长是【%v】\n",c.Perimeter()) 60 }
2.指针接受者
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 10 import ( 11 "fmt" 12 ) 13 14 type Point struct { 15 X,Y float64 16 } 17 18 func (p *Point) ScaleBy(factor float64) { //想要修改p的值就得传指针类型"*Point",注意,如果这里传递不是指针的话讲无法对其属性进行更改! 19 p.X *= factor // 等价于:X = X * factor,对对象P进行操作,修改其私有属性。 20 p.Y *= factor 21 } 22 23 func main() { 24 //两种调用方式: 25 p := Point{100,200} 26 p.ScaleBy(2) //姿势一:直接调用 27 fmt.Println(p) 28 29 p1 := Point{100,200} //姿势二:声明结构体后再用指针指向 30 p2 :=&p1 //使用结构体调用,再取其内存地址 31 p2.ScaleBy(2) 32 fmt.Println(p2) 33 } 34 35 36 37 #以上代码执行结果如下: 38 {200 400} 39 &{200 400}