【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。下面的例子展示了methodfunction之间的区别。

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)
}

renamerename1都是改变名字的方法,一个传引用,一个传值。只有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的方法?的更多相关文章

  1. 【Go语言入门系列】(八)Go语言是不是面向对象语言?

    [Go语言入门系列]前面的文章: [Go语言入门系列](五)指针和结构体的使用 [Go语言入门系列](六)再探函数 [Go语言入门系列](七)如何使用Go的方法? 1. Go是面向对象的语言吗? 在[ ...

  2. 【Go语言入门系列】(九)写这些就是为了搞懂怎么用接口

    [Go语言入门系列]前面的文章: [Go语言入门系列](六)再探函数 [Go语言入门系列](七)如何使用Go的方法? [Go语言入门系列](八)Go语言是不是面向对象语言? 1. 引入例子 如果你使用 ...

  3. Go语言入门系列(四)之map的使用

    本系列前面的文章: Go语言入门系列(一)之Go的安装和使用 Go语言入门系列(二)之基础语法总结 Go语言入门系列(三)之数组和切片 1. 声明 map是一种映射,可以将键(key)映射到值(val ...

  4. Go语言入门系列(五)之指针和结构体的使用

    Go语言入门系列前面的文章: Go语言入门系列(二)之基础语法总结 Go语言入门系列(三)之数组和切片 Go语言入门系列(四)之map的使用 1. 指针 如果你使用过C或C++,那你肯定对指针这个概念 ...

  5. Go语言入门系列(六)之再探函数

    Go语言入门系列前面的文章: Go语言入门系列(三)之数组和切片 Go语言入门系列(四)之map的使用 Go语言入门系列(五)之指针和结构体的使用 在Go语言入门系列(二)之基础语法总结这篇文章中已经 ...

  6. 【Go语言入门系列】Go语言工作目录介绍及命令工具的使用

    [Go语言入门系列]前面的文章: [保姆级教程]手把手教你进行Go语言环境安装及相关VSCode配置 [Go语言入门系列](八)Go语言是不是面向对象语言? [Go语言入门系列](九)写这些就是为了搞 ...

  7. Maven入门系列(二)--设置中央仓库的方法

    原文地址:http://www.codeweblog.com/maven入门系列-二-设置中央仓库的方法/ Maven仓库放在我的文档里好吗?当然不好,重装一次电脑,意味着一切jar都要重新下载和发布 ...

  8. Go语言入门系列2 基本语法

    get download and install packages and dependencies install = compile and install packages and depend ...

  9. Go语言入门系列1:安装,How to Write Go Code

    https://golang.org/doc/code.html src contains Go source files, pkg contains package objects, and bin ...

随机推荐

  1. 卷积神经网络 part1

    [任务一]视频学习心得及问题总结 根据下面三个视频的学习内容,写一个总结,最后列出没有学明白的问题. [任务二]代码练习 在谷歌 Colab 上完成代码练习,关键步骤截图,并附一些自己的想法和解读. ...

  2. Windows聚焦失效问题的解决办法

    1. 设置Windows聚焦 步骤:任务栏右键 → 任务栏设置 → 锁屏界面 → 背景选择Windows聚焦 2. 解决Windows聚焦失效问题 设置完Windows聚焦之后,锁屏界面却没有变. 尝 ...

  3. 2018-04-19:innodb和myisam区别

    福哥答案2020-04-19:

  4. C#LeetCode刷题之#232-用栈实现队列​​​​​​​​​​​​​​(Implement Queue using Stacks)

    问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/4108 访问. 使用栈实现队列的下列操作: push(x) -- ...

  5. Deep learning-based personality recognition from text posts of online social networks 阅读笔记

    文章目录 一.摘要 二.模型过程 1.文本预处理 1.1 文本切分 1.2 文本统一 2. 基于统计的特征提取 2.1 提取特殊的语言统计特征 2.2 提取基于字典的语言特征 3. 基于深度学习的文本 ...

  6. python使用zipfile递归压缩和解压缩文件

    import shutil,zipfile,os class ToolModel(object): def dfs_get_zip_file(self,input_path, result, igno ...

  7. JSONP跨域和CORS跨域的区别

    跨域: 由于浏览器中的javascript的同源策略,同源策略会阻止一个域的JavaScript脚本和另一个域的内容进行交互. 同源:协议,域名,端口,三者有一个不同即为跨域. 解决跨域有以下多种方法 ...

  8. IDEA使用教程(上)

     一.介绍 IDEA全称IntelliJ IDEA,是java语言开发的集成环境.idea提倡的是智能编码,目的是减少程序员的工作,其特色功能有智能的选取.丰富的导航模式.历史记录功能等,最突出的功能 ...

  9. 如何理解“异或(XOR)”运算在计算机科学中的重要性?(转自-阿里聚安全)

    XOR加密是一种简单高效.非常安全的加密方法 一. XOR 运算 逻辑运算之中,除了 AND 和 OR,还有一种 XOR 运算,中文称为"异或运算". 它的定义是:两个值相同时,返 ...

  10. 清空ARP缓存

    arp -n|awk '/^[1-9]/{print "arp -d " $1}'|sh -x