首页 > 其他分享 >Go 语言系列22:方法

Go 语言系列22:方法

时间:2022-11-25 13:39:33浏览次数:33  
标签:接收器 系列 name 22 Person age person Go fmt


方法其实在之前结构体那边简单讲过, 方法 其实就是一个函数,在 ​​func​​ 这个关键字和方法名中间加入了一个特殊的接收器类型。接收器可以是结构体类型或者是非结构体类型。接收器是可以在方法的内部访问的。

func (t Type) methodName(parameter list) {
}

上面的代码片段创建了一个接收器类型为 ​​Type​​​ 的方法 ​​methodName​​ 。

摘抄当时给结构体定义方法的例子,你就可能更加明白了。

package main

import "fmt"

// Person 定义一个名为 Person 的结构体
type Person struct {
name string
age int
}

// PrintPersonInfo 定义一个与 Person 的绑定的方法
func (person Person) PrintPersonInfo() {
fmt.Println("name:", person.name)
fmt.Println("age:", person.age)
}

func main() {
per13 := Person{
name: "John",
age: 30,
}
per13.PrintPersonInfo()
}

上面的程序中定义了一个与结构体 ​​Person​​​ 绑定的方法 ​​PrintPersonInfo()​​​ ,其中 ​​PrintPersonInfo​​​ 是方法名, ​​(person Person)​​​ 表示将此方法与 ​​Person​​​ 的实例绑定,这里我们把 ​​Person​​​ 称为方法的接收者,而 ​​person​​​ 表示实例本身,相当于 Python 中的 ​​self​​​ ,在方法内可以使用 ​​person.attribute_name​​ 来访问实例属性。运行该程序输出如下:

name: John
age: 30

当然,你可以把上面程序的方法改成一个函数,如下:

package main

import "fmt"

type Person struct {
name string
age int
}

func PrintPersonInfo(person Person) {
fmt.Println("name:", person.name)
fmt.Println("age:", person.age)
}

func main() {
per13 := Person{
name: "John",
age: 30,
}
PrintPersonInfo(per13)
}

运行这个程序,也同样会输出上面一样的答案,那么我们为什么还要用方法呢?因为在 Go 中,相同的名字的方法可以定义在不同的类型上,而相同名字的函数是不被允许的。如果你在上面这个程序添加一个同名函数,就会报错。但是在不同的结构体上面定义同名的方法就是可行的。

package main

import "fmt"

type Person struct {
name string
age int
}

func (person Person) PrintInfo() {
fmt.Println("person name:", person.name)
fmt.Println("person age:", person.age)
}

type City struct {
name string
}

func (city City) PrintInfo() {
fmt.Println("city name:", city.name)
}

func main() {
person := Person{name: "John", age: 30}
person.PrintInfo()
city := City{"Beijing"}
city.PrintInfo()
}

运行该程序输出如下:

person name: John
person age: 30
city name: Beijing



指针接收器与值接收器




值接收器和指针接收器之间的区别在于,在指针接收器的方法内部的改变对于调用者是可见的,然而值接收器的方法内部的改变对于调用者是不可见的,所以若要改变实例的属性时,必须使用指针作为方法的接收者。看看下面的例子就知道了:

package main

import "fmt"

// Person 定义一个名为 Person 的结构体
type Person struct {
name string
age int
}

// PrintPersonInfo 定义一个与 Person 的绑定的方法
func (person Person) PrintPersonInfo() {
fmt.Println("name:", person.name)
fmt.Println("age:", person.age)
}

func (person Person) ChangePersonName(name string) {
person.name = name
}

// AddPersonAge 定义一个与 Person 的绑定的方法,使 age 值加 n
func (person *Person) AddPersonAge(n int) {
person.age = person.age + n
}

func main() {
person := Person{
name: "John",
age: 30,
}
fmt.Println("before change")
person.PrintPersonInfo()

fmt.Println("after change")
person.AddPersonAge(5)
person.ChangePersonName("Mary")
person.PrintPersonInfo()
}

在上面的程序中, ​​AddPersonAge​​​ 使用指针接收器最终能改变实例的 ​​age​​​ 值,然而使用值接收器的 ​​ChangePersonName​​​ 最终没有改变实例 ​​name​​ 的值。运行该程序输出如下:

before change
name: John
age: 30
after change
name: John
age: 35

在方法中使用值接收器 与 在函数中使用值参数

当一个函数有一个值参数,它只能接受一个值参数。当一个方法有一个值接收器,它可以接受值接收器和指针接收器。

package main

import "fmt"

type Person struct {
name string
}

func (person Person) PrintInfo() {
fmt.Println(person.name)
}

func PrintInfo(person Person) {
fmt.Println(person.name)
}

func main() {
person := Person{"John"}
PrintInfo(person)
person.PrintInfo()

p := &person
//PrintInfo(p) // error
p.PrintInfo()
}

在上面的程序中,使用值参数 ​​PrintInfo(person)​​​ 来调用这个函数是合法的,使用值接收器来调用 ​​person.PrintInfo()​​ 也是合法的。

然后在程序中我们创建了一个指向 ​​person​​​ 的指针 ​​p​​​ ,通过使用指针接收器来调用 ​​p.PrintInfo()​​​ 是合法的,但使用值参数调用 ​​PrintInfo(p)​​ 是非法的。



在非结构体上的方法



不仅可以在结构体类型上定义方法,也可以在非结构体类型上定义方法,但是有一个问题。为了在一个类型上定义一个方法,方法的接收器类型定义和方法的定义应该在同一个包中。例如:

package main

import "fmt"

type myInt int

func (a myInt) add(b myInt) myInt {
return a + b
}

func main() {
var x myInt = 10
var y myInt = 20
fmt.Println(x.add(y)) // 30
}

参考文献:

[1] Alan A. A. Donovan; Brian W. Kernighan, Go 程序设计语言, Translated by 李道兵, 高博, 庞向才, 金鑫鑫 and 林齐斌, 机械工业出版社, 2017.



标签:接收器,系列,name,22,Person,age,person,Go,fmt
From: https://blog.51cto.com/u_15891283/5886554

相关文章

  • Go 语言系列24:go 协程
    Go协程是与其他函数或方法一起并发运行的函数或方法。Go协程可以看作是轻量级线程。与线程相比,创建一个Go协程的成本很小。因此在Go应用中,常常会看到有数以千计的Go......
  • Go 语言系列21:goto 无条件跳转
    在Go语言中保留​​goto​​​这点我确实没想到,毕竟很多人不建议使用​​goto​​​语句。​​goto​​后面接的是标签,表示下一步要执行哪里的代码。gotolabel...la......
  • one preparation of algorithms in short text clustering
    文本聚类算法文本聚类一般步骤文本表示(TextRepresentation)把文档表示成聚类算法可以处理的形式。聚类算法选择或设计(ClusteringAlgorithms)算法的选择,往往伴随着相......
  • Go语言错误总结(三)
    15、Strings无法修改尝试使用索引操作来更新字符串变量中的单个字符将会失败。string是只读的byteslice(和一些额外的属性)。如果你确实需要更新一个字符串,那么使用byteslic......
  • Go语言错误总结(二)
    8、使用“nil”SlicesandMaps在一个nil的slice中添加元素是没问题的,但对一个map做同样的事将会生成一个运行时的panic。正确代码:packagemainfuncmain(){vars[]i......
  • Go语言错误总结(五)
    29、未导出的结构体不会被编码以小写字母开头的结构体将不会被(json、xml、gob等)编码,因此当你编码这些未导出的结构体时,你将会得到零值。packagemainimport("encoding/......
  • Go语言错误总结(四)
    22、内建的数据结构操作不是同步的即使Go本身有很多特性来支持并发,并发安全的数据集合并不是其中之一,确保数据集合以原子的方式更新是你的职责。Goroutines和channels是实现......
  • Go 操作 MySQL 数据库
    这一期讲一讲如何使用Go操作MySQL数据库,这里就不讲MySQL的安装以及配置了,但要记得开启MySQL服务,我这里使用的是MySQL8.0.20版本。加载数据库驱动想要连接到数据......
  • Go 的 MySQL 预处理、MySQL 事务
    预处理是什么在普通SQL语句执行过程中,客户端会对SQL语句进行占位符替换,从而得到要执行的完整SQL语句,客户端再将此SQL语句发送到服务端执行,服务端最后把结果返回给客......
  • go实现投票并实时打印投票信息
    packagemainimport"fmt"varstudents[]Studentvarflagbool=truetypeStudentstruct{noint//候选人编号namestring//候选热姓名countint//得票数}fun......