前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >18.Go面向对象-方法

18.Go面向对象-方法

作者头像
Devops海洋的渔夫
发布2022-01-17 10:33:11
2170
发布2022-01-17 10:33:11
举报
文章被收录于专栏:Devops专栏

18.Go面向对象-方法

前言

在上一篇章我们使用匿名字段实现了继承,下面来看看如何设置面向对象-方法。

2 方法

2.1 基本方法创建

在介绍面向对象时,讲过可以通过属性和方法(函数)来描述对象。

什么是方法呢?

方法,大家可以理解成就是函数,但是在定义使用方面与前面讲解的函数还是有区别的。

我们先定义一个传统的函数:

代码语言:javascript
复制
func Test(a, b int) int {
   return a + b
}

func main() {
   result := Test(1, 2)
   fmt.Println(result)
}

这个函数非常简单,下面定义一个方法,看一下在语法与传统的函数有什么区别:

方法的定义:
代码语言:javascript
复制
type Integer int // 为 int 定义别名 Integer

func (a Integer) Test(b Integer) Integer {
   return a + b
}

func main() {
   var result Integer = 3
   r := result.Test(3)
   fmt.Println(r)
}

type Integer int:表示的意思是给int类型指定了一个别名叫Integer,别名可以随便起,只要符合GO语言的命名规则就可以。

指定别名后,后面可以用Integer来代替int 来使用。

代码语言:javascript
复制
func (a Integer) Test(b Integer) Integer {
   return a + b
}
方法的定义与函数的区别

表示定义了一个方法,方法的定义与函数的区别

第一:在关键字后面加上 ( a Integer), 这个在方法中称之为接收者,所谓的接受者就是接收传递过来的第一个参数,然后复制aa的类型是Integer ,由于Integerint的别名,所以a的类型为int

第二:在表示参数的类型时,都使用了对应的别名。

通过方法的定义,可以看出方法其实就是给某个类型绑定的函数。在该案例中,是为整型绑定的函数,只不过在给整型绑定函数(方法)时,一定要通过type来指定一个别名,因为int类型是系统已经规定好了,无法直接绑定函数,所以只能通过别名的方式。

第三:调用方式不同

代码语言:javascript
复制
var result Integer = 3

表示定义一个整型变量result,并赋值为3.

代码语言:javascript
复制
result.Test(3)

通过result变量,完成方法的调用。

因为Test()方法,是为int类型绑定的函数,而result变量为int类型,所以可以调用Test()方法。

result变量的值会传递给Test()方法的接受者,也就是参数a,而实参Test(3),会传递形参b.

当然,我们也可以将Test()方法,理解成是为int类型扩展了,追加了的方法。因为系统在int类型时,是没有该方法的。

通过以上的定义,发现方法其实就是函数的语法糖。

在以上案例中,Test()方法是为int类型绑定的函数,所以任何一个整型变量,都可以调用该方法。

例如:

代码语言:javascript
复制
var sum Integer = 6
t := sum.Test(8)
fmt.Println(t)

// 执行
14

2.2 给结构体添加方法

上面给整型创建了一个方法,那么直接通过整型变量加上“点”,就可以调用该方法了。

大家想一下,如果给结构体(类)加上了方法,那么根据结构体(类)创建完成对象后,是不是就可以通过对象加上“点”,就可以完成方法的调用,这与调用类中定义的属性的方式是完全一样的。这样就完成了通过方法与属性来描述一个对象的操作。(大家自己回想一下,前面在讲解对象的概念时,一直在说其具有的属性与行为(方法))

结构体添加方法

给结构体添加方法,语法如下:

代码语言:javascript
复制
type Student struct {
   id int
   name string
   age int
   score float64
}

func (stu Student) PrintShow()  { // 定义Student结构体的方法PrintShow
   fmt.Println(stu)
}

func main() {
   student := Student{101, "zhangsan", 19, 90}
   student.PrintShow() // 调用结构体的方法
} 

给结构体添加方法的方式与前面给int类型添加方法的方式,基本一致。

唯一不同的是,不需要给结构体指定别名,因为结构体Student就是相当于其所有成员属性的别名(id,name,score),所以这里不要在给结构体Student创建别名。

调用方式:根据结构体(类)创建的对象,完成了方法的调用。

修改结构体方法的接收者类型为指针,则允许修改对应的值

PrintShow()方法的作用,只是将结构体的成员(属性)值打印出来,如果要修改其对应的值,应该怎么做呢?

这时,大家肯定想起来指针,将方法的接收者,修改成对应的指针类型。

具体修改如下:

代码语言:javascript
复制
type Student struct {
   id    int
   name  string
   age   int
   score float64
}

// 定义Student结构体的方法PrintShow
func (stu Student) PrintShow() { 
   fmt.Println(stu)
}

// 接收者类型为指针 (p *Student)
func (p *Student) EditInfo(id int, name string, age int, score float64) {
   p.id = id
   p.name = name
   p.age = age
   p.score = score
}

func main() {
   //student := Student{101, "zhangsan", 19, 90}
   var student Student
   (&student).EditInfo(102,"lisi",19,88) // 将结构体的指针传递给接收者,调用修改数据的方法
   student.PrintShow() // 调用结构体的方法
} 

在创建方法时,接收者类型为指针类型,所以在调用方法时,创建一个结构体变量,同时将结构体变量的地址,传递给方法的接收者,然后调用EditInfo()方法,完成要修改的数据传递。

在使用方法是,要注意如下几个问题:

第一:只要接收者类型不一样,这个方法就算同名,也是不同方法,不会出现重复定义函数的错误

代码语言:javascript
复制
type long int

func (tmp long) test()  {
   
}

type char byte

func (tmp char) test()  {
   
} 

但是,如果接收者类型一样,但是方法的参数不一样,是会出现错误的。

也就是,在GO中没有方法重载(所谓重载,指的是方法名称一致,参数类型,个数不一致)。


第二:关于接收者不能为指针类型。

代码语言:javascript
复制
type long int

func (tmp *long) test()  {

} 

以上定义正确

image-20210531082214366


第三:接收者为普通变量,非指针,值传递

代码语言:javascript
复制
type Student struct {
   id    int
   name  string
   age   int
   score float64
}

// 定义Student结构体的方法PrintShow
func (stu Student) PrintShow(id int, name string, age int, score float64) { 
 stu.id = id
 stu.name = name
 stu.age = age
 stu.score = score
}

func main() {
 var stu Student
 stu.PrintShow(102,"lisi",19,88)
 fmt.Println(stu)
}

// 执行:因为传递的不是内存地址,所以无法修改原 stu 的值
{0  0 0}

接收者为指针变量,引用传递

代码语言:javascript
复制
func (p *Student) EditInfo(id int, name string, age int, score float64) {
   p.id = id
   p.name = name
   p.age = age
   p.score = score
}

func main() {
   var stu Student
   (&stu).EditInfo(102,"lisi",19,88) // 将结构体的指针传递给接收者,调用修改数据的方法
   fmt.Println(stu)
}  
 
// 执行:
{102 lisi 19 88}

2.3 指针变量的方法值

在上面的案例中,我们定义了两个方法,一个是PrintShow(), 该方法的接收者为普通方法,一个EditInfo()方法,该方法的接收者为指针变量,那么大家思考这么一个问题:定义一个结构体指针变量,能否调用PrintShow()方法呢?

如下所示:

代码语言:javascript
复制
type Student struct {
   id    int
   name  string
   age   int
   score float64
}

func (stu Student) PrintShow(id int, name string, age int, score float64) { // 定义Student结构体的方法PrintShow
   stu.id = id
   stu.name = name
   stu.age = age
   stu.score = score
   fmt.Println("PrintShow:", stu)
}

func (p *Student) EditInfo(id int, name string, age int, score float64) {
   p.id = id
   p.name = name
   p.age = age
   p.score = score
   fmt.Println("EditInfo:", *p)
}

func main() {
   var stu Student // 定义一个结构体类型的变量
   (&stu).PrintShow(101,"zhangsan",19,90) // 为什么结构体指针变量,可以调用`PrintShow()`方法呢?
   (&stu).EditInfo(102,"lisi",20,67)
}

// 执行如下:
PrintShow: {101 zhangsan 19 90}
EditInfo: {102 lisi 20 67} 

通过测试,发现是可以调用的。

结构体指针 可以 调用 普通结构体变量 的方法

为什么结构体指针变量,可以调用PrintShow()方法呢?

原因是:先将指针stu,转换成*stu在调用。

等价如下代码:

代码语言:javascript
复制
(*(&stu)).PrintShow(101,"zhangsan",19,90) 

所以,如果结构体变量是一个指针变量,它能够调用哪些方法,这些方法就是一个集合,简称方法集

普通结构体变量 可以 调用 结构体指针 的方法

如果是普通的结构体变量能否调用EditInfo()方法。

代码语言:javascript
复制
type Student struct {
   id    int
   name  string
   age   int
   score float64
}

func (stu Student) PrintShow(id int, name string, age int, score float64) { 
   stu.id = id
   stu.name = name
   stu.age = age
   stu.score = score
   fmt.Println("PrintShow:", stu)
}

func (p *Student) EditInfo(id int, name string, age int, score float64) {
   p.id = id
   p.name = name
   p.age = age
   p.score = score
   fmt.Println("EditInfo:", *p)
}

func main() {
   var stu Student 
   stu.EditInfo(102,"lisi",20,67) // 普通结构体变量可以调用 EditInfo
} 

是可以调用的,原因是:将普通的结构体类型的变量转换成(&stu)在调用EditInfo()方法。

“这样的好处是非常灵活,创建完对应的对象后,可以随意调用方法,不需要考虑太多指针的问题。 ”

2.4 练习题

下面进行面向对象编程的练习

练习1:

定义一个学生类,有六个属性,分别为姓名、性别、年龄、语文、数学、英语成绩。

有2个方法:

一个打招呼的方法:介绍自己叫XX,今年几岁了。是男同学还是女同学。

两个计算自己总分数和平均分的方法。{显示:我叫XX,这次考试总成绩为X分,平均成绩为X分}

  • 1.结构体定义如下:
代码语言:javascript
复制
type Student struct {
   name    string  // 姓名
   sex     byte    // 性别
   age     int     // 年龄
   chinese float64 // 语文成绩
   math    float64 // 数学成绩
   english float64 // 英语成绩
}
  • 2.为结构体定义相应的方法,并且在方法中可以完成对传递过来的数据的校验
代码语言:javascript
复制
func (p *Student) SayHello(name string, age int, sex byte) {
   p.name = name
   p.age = age
   p.sex = sex
   // 对年龄与性别进行判断
   if p.age < 0 && p.age > 100 {
      p.age = 0
   }
   if p.sex != 'm' && p.sex != 'w' {
      p.sex = 'm'
   }
   fmt.Printf("我叫%s, 我今年%d岁了,我是%c生。", p.name, p.age, p.sex)
}

func (p *Student) ShowScore(chinese float64, math float64, english float64) {
   p.chinese = chinese
   p.math = math
   p.english = english
   var sum float64
   sum = p.chinese + p.math + p.english
   fmt.Printf("我叫%s, 我的总成绩是%.2f, 平均成绩是%.2f", p.name, sum, sum/3)
}
  • 3.完成方法的调用
代码语言:javascript
复制
var student Student
student.SayHello("zhangsan", 11,'m')
student.ShowScore(90,85,97)

// 执行:
我叫zhangsan, 我今年11岁了,我是m生。我叫zhangsan, 我的总成绩是272.00, 平均成绩是90.67

在以上的案例中,SayHello()方法中已经完成了name属性的赋值,所以在ShowScore()方法中,可以直接使用,因为我们使用指针指向了同一个结构体内存。

在调用的过程中,也能体会出确实很方便,,不需要考虑太多指针的问题

练习2:

写一个Ticket类,有一个距离属性, 不能为负数,有一个价格属性, 并且根据距离distance计算价格Price (1元/公里):

0-100公里 票价不打折

101-200公里 总额打9.5折

201-300公里 总额打9折

300公里以上 总额打8折

实现如下:

代码语言:javascript
复制
/**
Ticket类
- 0-100公里    票价不打折
- 101-200公里   总额打9.5折
- 201-300公里   总额打9折
- 300公里以上   总额打8折
*/
type Ticket struct {
   distance float64 // 距离
   price    float64 // 价格
}

func (p *Ticket) GetPrice(price float64, distance float64) {
   if distance < 0 {
      distance = 0
   }
   p.distance = distance
   p.price = price
   if p.distance > 0 && p.distance <= 100 {
      p.price = p.price * 1.0
   } else if p.price >= 101 && p.price < 200 {
      p.price = p.price * 0.95
   } else if p.price >= 201 && p.price < 300 {
      p.price = p.price * 0.9
   } else {
      p.price = p.price * 0.8
   }
   fmt.Println(p.price)
}

func main() {
   var ticket Ticket
   ticket.GetPrice(190, 200)
}

// 执行:
180.5
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-08-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 海洋的渔夫 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 18.Go面向对象-方法
  • 前言
  • 2 方法
    • 2.1 基本方法创建
      • 什么是方法呢?
      • 方法的定义:
      • 方法的定义与函数的区别
    • 2.2 给结构体添加方法
      • 结构体添加方法
      • 修改结构体方法的接收者类型为指针,则允许修改对应的值
      • 在使用方法是,要注意如下几个问题:
    • 2.3 指针变量的方法值
      • 结构体指针 可以 调用 普通结构体变量 的方法
      • 普通结构体变量 可以 调用 结构体指针 的方法
    • 2.4 练习题
      • 练习1:
      • 练习2:
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档