【Go语言入门系列】(七)如何使用Go的方法?
【Go语言入门系列】前面的文章:
本文介绍Go语言的方法的使用。
1. 声明
如果你用过面向对象的语言,比如Java,那你肯定对类、对象、成员变量、方法等很熟悉。
简单地来说,类是对一类事物的抽象,成员变量是该事物的属性,方法是该事物具有的行为,对象则是该事物所对应的具体个体。
比如说,狗(类),名字(属性),叫(方法),哮天犬(对象)。
但是Go语言中并没有类,自然也没有面向对象中的成员变量和成员方法。但是Go语言中有类似的概念——结构体,结构体中的字段可以看做类中成员属性。
Go中也有类似于面向对象中方法的概念,也叫方法(method
),这种方法其实是一种特殊的函数(function
)——带有接收者(receiver
)的函数。
方法的声明方式如下:
func (接受者) funcName(参数们) (返回值们)
可以看出方法的声明方式和函数的声明方式差不多,但是多了一个接收者,该接收者是一个结构体类型。下面是一个实例:
package main
import "fmt"
type dog struct {
name string
}
func (d dog) say() {//方法
fmt.Println(d.name + " 汪汪汪。。。方法")
}
func main() {
d := dog{"哮天犬"}
d.watchDoor()
}
运行:
哮天犬 汪汪汪。。。方法
say()
是一个方法,d
是接收者,是一个结构体类型参数,方法里可以访问接收者的字段:
fmt.Println(d.name + " 汪汪汪。。。方法")
通过.
可以调用方法:
d.say()
2. 方法和函数
方法method
是具有接收者receiver
的特殊函数function
。下面的例子展示了method
和function
之间的区别。
package main
import "fmt"
type dog struct {
name string
}
func (d dog) say() {
fmt.Println(d.name + " 汪汪汪。。。方法")
}
func say(d dog) {
fmt.Println(d.name + " 汪汪汪。。。函数")
}
func main() {
d := dog{"哮天犬"}
d.watchDoor()
watchDoor(d)
}
运行:
哮天犬 汪汪汪。。。方法
哮天犬 汪汪汪。。。函数
你可能会问,在这个例子中,既然方法和函数的运行结果一样,那使用方法岂不是多此一举,为何不继续使用函数?
换一个场景:现在有狗、猫、兔子等动物,他们都会叫,只是叫声不同:
package main
import "fmt"
type dog struct {
name string
}
type cat struct {
name string
}
type rabbit struct {
name string
}
func dogSay(d dog) {
fmt.Println(d.name + " 汪汪汪。。。函数")
}
func catSay(c cat) {
fmt.Println(c.name + " 喵喵喵。。。函数")
}
func rabbitSay(r rabbit) {
fmt.Println(r.name + " 吱吱吱。。。函数")
}
func main() {
d := dog{"哮天犬"}
c := cat{"加菲猫"}
r := rabbit{"玉兔"}
dogSay(d)
catSay(c)
rabbitSay(r)
}
运行:
哮天犬 汪汪汪。。。函数
加菲猫 喵喵喵。。。函数
玉兔 吱吱吱。。。函数
上面的三个函数有什么不妥之处呢?
首先,这三个函数都是用来表示叫
这一行为,一般来说函数名都会叫say()
,但因为不同的动物,函数名不能相同,为了做区别而做出了改变。
其次,叫
这个行为应该属于动物,二者在概念上不能分开。比如,说话这个行为是每个人都具有的,但是说话并不能离开人而独自存在。
此时,方法method
的优点就体现了出来:
package main
import "fmt"
type dog struct {
name string
}
type cat struct {
name string
}
type rabbit struct {
name string
}
func (d dog) say() {
fmt.Println(d.name + " 汪汪汪。。。方法")
}
func (c cat) say() {
fmt.Println(c.name + " 喵喵喵。。。方法")
}
func (r rabbit) say() {
fmt.Println(r.name + " 吱吱吱。。。方法")
}
func main() {
d := dog{"哮天犬"}
c := cat{"加菲猫"}
r := rabbit{"玉兔"}
d.say() //调用
c.say()
r.say()
}
运行:
哮天犬 汪汪汪。。。方法
加菲猫 喵喵喵。。。方法
玉兔 吱吱吱。。。方法
三个方法的方法名都一样,每个方法都有一个接受者receiver
,这个receiver
使方法在概念上属于结构体,就像结构体的字段一样,但是没有写在结构体内。
从这三个方法中可以看出:只要方法的接收者不同,即使方法名相同,方法也不相同。
3. 指针和接收者
接收者可以使用指针,和函数的参数使用指针一样(参考Go语言入门系列(六)之再探函数),接收者使用指针传的是引用,不使用指针传的是值拷贝。看下面一个例子:
package main
import "fmt"
type dog struct {
name string
}
func (d *dog) rename(name string) {
d.name = name
fmt.Println("方法内:" + d.name)
}
func (d dog) rename1(name string) {
d.name = name
fmt.Println("方法内:" + d.name)
}
rename
和rename1
都是改变名字的方法,一个传引用,一个传值。只有rename
能真正改变名字。
func main() {
d := dog{"哮天犬"}
d.rename("小黑黑")
fmt.Println(d.name)
}
运行:
方法内:小黑黑
小黑黑
rename
把“哮天犬”改为了“小黑黑”。
func main() {
d := dog{"哮天犬"}
d.rename1("小红红")
fmt.Println(d.name)
}
运行:
方法内:小红红
哮天犬
rename1
只在方法内改变了名字,并没有真正改变“哮天犬”。因为rename1
接收的是d
的一个拷贝。
方法的指针接收者可以进行重定向,什么意思呢?下面用四段代码来说明。
如果函数的参数是一个指针参数,那么该函数就必须接收一个指针才行,如果是值则报错:
package main
import "fmt"
func double(x *int) {
*x = *x * 2
}
func main() {
i := 2
double(&i) //编译正确
double(i) //报错
fmt.Println(i)
}
而如果方法的接收者是一个指针,那么该方法被调用时,接收者既可以是指针,又可以是值:
package main
import "fmt"
func (d *dog) rename(name string) {
d.name = name
fmt.Println("方法内:" + d.name)
}
func main() {
d := dog{"哮天犬"}
d.rename("小黑黑") //接收者是值,编译正确
//(&d).rename("小黑黑") //接收者是指针,编译正确
fmt.Println(d.name)
}
对于指针接收者来说,d.rename("小黑黑")
被解释为(&d).rename("小黑黑")
,如此一来,我们就不需要在意调用方法的接收者是否为指针类型,因为Go会进行“重定向”。
同理,反过来也可以。
如果函数的参数是值,而不是指针,那么该函数必须接受值,否则会报错:
package main
import "fmt"
func double(x int) {
x = x * 2
}
func main() {
i := 2
p := &i
double(*p) //参数是值,编译正确
//double(p) //参数是指针,报错
fmt.Println(i)
}
而如果方法的接收者是一个值,那么该方法被调用时,接收者既可以是值,又可以是指针:
package main
import "fmt"
func (d dog) rename1(name string) {
d.name = name
fmt.Println("方法内:" + d.name)
}
func main() {
d := dog{"哮天犬"}
p := &d
p.rename1("小红红") //接收者是指针,编译正确
//(*p).rename1("小红红") //接收者是值,编译正确
fmt.Println(d.name)
}
对于值接收者来说,p.rename1("小红红")
被解释为(*p).rename1("小红红")
,如此一来,我们就不需要在意调用方法的接收者是否为值,因为Go会进行“重定向”。
作者简介
我是行小观,我会在公众号『行人观学』中持续更新Java、Go、数据结构和算法、计算机基础等相关文章。
本文章属于系列文章「Go语言入门系列」,本系列从Go语言基础开始介绍,适合从零开始的初学者。
欢迎关注,我们一起踏上行程。
如有错误,还请指正。
【Go语言入门系列】(七)如何使用Go的方法?的更多相关文章
- 【Go语言入门系列】(八)Go语言是不是面向对象语言?
[Go语言入门系列]前面的文章: [Go语言入门系列](五)指针和结构体的使用 [Go语言入门系列](六)再探函数 [Go语言入门系列](七)如何使用Go的方法? 1. Go是面向对象的语言吗? 在[ ...
- 【Go语言入门系列】(九)写这些就是为了搞懂怎么用接口
[Go语言入门系列]前面的文章: [Go语言入门系列](六)再探函数 [Go语言入门系列](七)如何使用Go的方法? [Go语言入门系列](八)Go语言是不是面向对象语言? 1. 引入例子 如果你使用 ...
- Go语言入门系列(四)之map的使用
本系列前面的文章: Go语言入门系列(一)之Go的安装和使用 Go语言入门系列(二)之基础语法总结 Go语言入门系列(三)之数组和切片 1. 声明 map是一种映射,可以将键(key)映射到值(val ...
- Go语言入门系列(五)之指针和结构体的使用
Go语言入门系列前面的文章: Go语言入门系列(二)之基础语法总结 Go语言入门系列(三)之数组和切片 Go语言入门系列(四)之map的使用 1. 指针 如果你使用过C或C++,那你肯定对指针这个概念 ...
- Go语言入门系列(六)之再探函数
Go语言入门系列前面的文章: Go语言入门系列(三)之数组和切片 Go语言入门系列(四)之map的使用 Go语言入门系列(五)之指针和结构体的使用 在Go语言入门系列(二)之基础语法总结这篇文章中已经 ...
- 【Go语言入门系列】Go语言工作目录介绍及命令工具的使用
[Go语言入门系列]前面的文章: [保姆级教程]手把手教你进行Go语言环境安装及相关VSCode配置 [Go语言入门系列](八)Go语言是不是面向对象语言? [Go语言入门系列](九)写这些就是为了搞 ...
- Maven入门系列(二)--设置中央仓库的方法
原文地址:http://www.codeweblog.com/maven入门系列-二-设置中央仓库的方法/ Maven仓库放在我的文档里好吗?当然不好,重装一次电脑,意味着一切jar都要重新下载和发布 ...
- Go语言入门系列2 基本语法
get download and install packages and dependencies install = compile and install packages and depend ...
- Go语言入门系列1:安装,How to Write Go Code
https://golang.org/doc/code.html src contains Go source files, pkg contains package objects, and bin ...
随机推荐
- MediaDevices对象
mediaDevices 是 Navigator对象的一个 只读属性,返回一个 MediaDevices 对象,该对象可提供对相机和麦克风等媒体输入设备的连接访问,也包括屏幕共享. 语法 const ...
- 实战:docker搭建FastDFS文件系统并集成SpringBoot
实战:docker搭建FastDFS文件系统并集成SpringBoot 前言 15年的时候,那时候云存储还远远没有现在使用的这么广泛,归根结底就是成本和安全问题,记得那时候我待的公司是做建站开发的,前 ...
- Maven执行Reimport命令报错:See logs for details
Idea版本:2018.1.3 maven版本:3.6.2 Idea配置了本地下载的maven之后,不管是直接执行maven的Reimport命令,还是使用idea的Reimport按钮,都会报这个错 ...
- Python实现图片滑动式验证识别
1 abstract 很多人学习python,不知道从何学起.很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手.很多已经做案例的人,却不知道如何去学习更加高深的知识.那么针对这三类 ...
- 解决SpringBoot项目中Thymeleaf模板的中文乱码问题
1.使用IDEA创建SpringBoot项目 package com.example.demo; import org.springframework.boot.SpringApplication; ...
- java_内部类、匿名内部类的使用
内部类 将一个类A定义在另一个类B里面,里面的那个类A就称为内部类,B则称为外部类. 内部类的分类 成员内部类,类定义在了成员位置 (类中方法外称为成员位置) 局部内部类,类定义在方法内 成员内部类 ...
- 【模式识别与机器学习】——logistic regression
虽然叫做“回归”,但是这个算法是用来解决分类问题的.回归与分类的区别在于:回归所预测的目标量的取值是连续的(例如房屋的价格):而分类所预测的目标变量的取值是离散的(例如判断邮件是否为垃圾邮件).当然, ...
- C++二分查找:lower_bound( )和upper_bound( )
#include<algorithm>//头文件 //标准形式 lower_bound(int* first,int* last,val); upper_bound(int* first, ...
- 2020-06-20:一句话总结ZK?
福哥答案2020-06-20: 这道题价值不大,但是面试题里有这道题. 分布式协调服务,注册服务和发现,树形结构,监听机制,过半机制. ZooKeeper是源代码开放的分布式协调服务,由雅虎公司创建, ...
- 从系统报表页面导出20w条数据到本地只用了4秒,我是如何做到的
背景 最近有个学弟找到我,跟我描述了以下场景: 他们公司内部管理系统上有很多报表,报表数据都有分页显示,浏览的时候速度还可以.但是每个报表在导出时间窗口稍微大一点的数据时,就异常缓慢,有时候多人一起导 ...