【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. 排序HEOI2016/TJOI2016 二分+线段树判定

    LINK:排序 此题甚好我一点思路都没有要是我当时省选此题除了模拟我恐怕想不到还可以二分 还可以线段树... 有点ex 不太好写 考虑 暴力显然每次给出询问我们都是可以直接sort的 无视地形无视一切 ...

  2. 剑指 Offer 52. 两个链表的第一个公共节点

    题目链接 题目描述: 我的题解: 方法一:双指针法 思路分析: 声明两个指针p1,p2 分别指向链表A.链表B. 然后分别同时逐结点遍历 当 p1 到达链表 headA 的末尾时,重新定位到链表 he ...

  3. JavaWeb项目的部署以及远程调试

    Linux环境下软件的安装 Linux环境下的程序的安装.更新.卸载和查看. rpm 命令:相当于windows程序的添加/卸载程序,进程程序的安装,查看,卸载. 本地程序安装:rpm -ivh 程序 ...

  4. stl_heap

    学习一下stl_heap 下面的算法是根据stl源码重新整合一下,是为了方便理解 因为使用的迭代器,为了在给定的迭代器之间使用heap的一些方法, 内部通常用disHole来确定某个节点 dishol ...

  5. 前端面试基础题:Ajax原理

    Ajax 的原理简单来说是在⽤户和服务器之间加了—个中间层( AJAX 引擎),通过XmlHttpRequest 对象来向服务器发异步请求,从服务器获得数据,然后⽤ javascrip t 来操作 D ...

  6. 华为云的研究成果又双叒叕被MICCAI收录了!

    摘要:2020年国际医学图像计算和计算机辅助干预会议(MICCAI 2020),论文接收结果已经公布:华为云医疗AI团队和华中科技大学合作的2篇研究成果入选. 语义/实例分割问题是近年来医学图像计算领 ...

  7. Kubernetes 使用arthas进行调试

    环境 因为k8s中是最基本的jre,网上说缺少tools.jar,但是补充了以后还是不行,最后还是将整个jdk给移到容器中的. jre中执行: /home # /opt/jre/bin/java -j ...

  8. RF,SVM和NN的优缺点

    1. 随机森林优缺点 随机森林(RF)是Bagging的一个扩展变体.RF在以决策树为基分类器进行集成的基础上,进一步在决策树的训练过程中引入了随机属性选择. Bagging就是对数据集训练多个基分类 ...

  9. MySQL数据库——数据约束

    1 什么数据约束 对用户操作表的数据进行约束 2 默认值(缺省约束) 作用: 当用户对使用默认值的字段不插入值的时候,就使用默认值. 注意: 1)对默认值字段插入null是可以的 2)对默认值字段可以 ...

  10. java final关键字与static关键字

    一  final关键字 1.final修饰类不可以被继承,但是可以继承其他类. 例如: class Yy {} final class Fu extends Yy{} //可以继承Yy类 class ...