二丫讲梵 二丫讲梵
首页
  • 最佳实践
  • 迎刃而解
  • Nginx
  • Php
  • Zabbix
  • AWS
  • Prometheus
  • Grafana
  • CentOS
  • Systemd
  • Docker
  • Rancher
  • Ansible
  • Ldap
  • Gitlab
  • GitHub
  • Etcd
  • Consul
  • RabbitMQ
  • Kafka
  • MySql
  • MongoDB
  • OpenVPN
  • KVM
  • VMware
  • Other
  • ELK
  • K8S
  • LLM
  • Nexus
  • Jenkins
  • 随写编年
  • 家人物语
  • 追忆青春
  • 父亲的朋友圈
  • 电影音乐
  • 效率工具
  • 博客相关
  • Shell
  • 前端实践
  • Vue学习笔记
  • Golang学习笔记
  • Golang编程技巧
  • 学习周刊
  • Obsidian插件周刊
关于
友链
  • 本站索引

    • 分类
    • 标签
    • 归档
  • 本站页面

    • 导航
    • 打赏
  • 我的工具

    • 备忘录清单 (opens new window)
    • json2go (opens new window)
    • gopher (opens new window)
    • 微信MD编辑 (opens new window)
    • 国内镜像 (opens new window)
    • 出口IP查询 (opens new window)
    • 代码高亮工具 (opens new window)
  • 外站页面

    • 开往 (opens new window)
    • ldapdoc (opens new window)
    • HowToStartOpenSource (opens new window)
    • vdoing-template (opens new window)
GitHub (opens new window)

二丫讲梵

行者常至,为者常成
首页
  • 最佳实践
  • 迎刃而解
  • Nginx
  • Php
  • Zabbix
  • AWS
  • Prometheus
  • Grafana
  • CentOS
  • Systemd
  • Docker
  • Rancher
  • Ansible
  • Ldap
  • Gitlab
  • GitHub
  • Etcd
  • Consul
  • RabbitMQ
  • Kafka
  • MySql
  • MongoDB
  • OpenVPN
  • KVM
  • VMware
  • Other
  • ELK
  • K8S
  • LLM
  • Nexus
  • Jenkins
  • 随写编年
  • 家人物语
  • 追忆青春
  • 父亲的朋友圈
  • 电影音乐
  • 效率工具
  • 博客相关
  • Shell
  • 前端实践
  • Vue学习笔记
  • Golang学习笔记
  • Golang编程技巧
  • 学习周刊
  • Obsidian插件周刊
关于
友链
  • 本站索引

    • 分类
    • 标签
    • 归档
  • 本站页面

    • 导航
    • 打赏
  • 我的工具

    • 备忘录清单 (opens new window)
    • json2go (opens new window)
    • gopher (opens new window)
    • 微信MD编辑 (opens new window)
    • 国内镜像 (opens new window)
    • 出口IP查询 (opens new window)
    • 代码高亮工具 (opens new window)
  • 外站页面

    • 开往 (opens new window)
    • ldapdoc (opens new window)
    • HowToStartOpenSource (opens new window)
    • vdoing-template (opens new window)
GitHub (opens new window)
  • Shell编程

  • Go编程笔记

  • 前端编程笔记

  • Go学习笔记

    • 基础部分

      • 基础知识
      • 数据类型
      • fmt的格式化输入输出
      • 运算符
      • 流程控制
      • 认识函数
      • 包的操作
      • 项目工程-在同级目录
      • 指针(pointer)
      • 数组(array)
      • 切片(slice)
      • 字典(map)
      • 结构体(struct)
      • 匿名组合
      • 方法
        • 1,概述
        • 2,面向对象和面向过程。
        • 3,结构体类型添加方法。
        • 4,值语义和指针语义。
        • 5,方法集。
          • 1,指针类型方法集。
          • 2,普通类型方法集。
        • 6,匿名字段。
          • 1,方法的继承。
          • 2,方法的重写。
          • 3,方法值。
          • 4,方法表达式。
      • 接口
      • error接口
      • panic
      • recover
      • 字符串操作
      • 字符串转换
      • 正则表达式
      • JSON处理
      • 文件的操作
      • goroutine
      • channel
      • select
      • sync-lock锁
      • 网络概述
      • Socket编程
      • 发文件与聊天室
      • http编程
      • 爬虫实战
      • 单元测试了解
    • web框架

    • orm框架

  • Vue-21年学习笔记

  • Vue-22年重学笔记

  • 编程世界
  • Go学习笔记
  • 基础部分
二丫讲梵
2021-07-10
目录

方法

文章发布较早,内容可能过时,阅读注意甄别。

# 1,概述

在面向对象编程中,一个对象其实也就是一个简单的值或者一个变量,在这个对象中会包含一些函数,这种带有接收者的函数,我们称之为方法(method)。本质上,一个方法则是一个和特殊类型关联的函数。

一个面向对象的程序会用方法来表达其属性和对应的操作,这样使用这个对象的用户就不需要直接去操作对象,而是借助方法来做这些事情。

在 Go 语言中,可以给任意自定义类型(包括内置类型,但不包括指针类型)添加相应的方法。

方法总是绑定对象实例,并隐式将实例作为第一实参(receiver),方法的语法如下:

	func (receiver ReceiverType) funcName(parameters) (results)
1
  • 参数 receiver 可任意命名。如方法中未曾使用,可省略参数名。
  • 参数 receiver 类型可以是 T 或*T。基类型 T 不能是接口或指针,否则将编译失败。
  • 不支持重载方法,也就是说,不能定义名字相同但是不同参数的方法。
  • 只要接收者类型不一样,即便出现两个方法同名的情况,也表示不同的方法,不会出现重复定义函数的错误。

# 2,面向对象和面向过程。

package main

import "fmt"

//实现2数相加
//面向过程
func Add01(a, b int) int {
	return a + b

}

//面向对象,通过方法,就是给某个类型绑定一个函数
type long int

//tmp叫接收者,接收者就是传递一个参数
func (tmp long) Add02(other long) long {
	return tmp + other
}

func main() {
	var s1 int
	s1 = Add01(2, 3)
	fmt.Println("s1 = ", s1)

	//定义一个变量
	var s2 long = 2
	//调用方法格式: 变量名.函数(所需参数)
	r := s2.Add02(3)
	fmt.Println("r = ", r)
	//面向对象只是换了一种表现形式
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

# 3,结构体类型添加方法。

package main

import "fmt"

type Person struct {
	name string
	sex  byte
	age  int
}

//带有接收者的函数叫做方法
func (tmp Person) PrintInfo() {
	fmt.Println("tmp = ", tmp)
}

//通过一个函数,利用指针,给成员赋值
func (p *Person) SetInfo(n string, s byte, a int) {
	p.name = n
	p.sex = s
	p.age = a
}

//注意,如果基础变量时指针类型,那么不能被方法调用
//long为接收者类型,他本身不能够是指针类型。
//type long *int
//func (a long) test(){
//
//}

func main() {
	//定义同时初始化
	p := Person{"mike", 'm', 18}
	//这里的p对应tmp,.PrintInfo表示调用后边的函数
	p.PrintInfo()

	//定义一个结构体变量
	var p1 Person
	(&p1).SetInfo("yoyo", 'g', 22)
	p1.PrintInfo()

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

# 4,值语义和指针语义。

其实就是前边一直有提到的普通引用传递,以及指针引用传递。

package main

import "fmt"

type Person struct {
	name string
	sex  byte
	age  int
}

//修改成员变量的值

//接收者为普通变量,非指针,也叫值语义,相当于拷贝一份,与原来的互不相干
func (p Person) SetInfoValue(n string, s byte, a int) {
	p.name = n
	p.sex = s
	p.age = a
	fmt.Println("p = ", p)
	fmt.Printf("SetInfoValue &p = %p\n", &p)
}

//接收者为指针变量,就是引用传递,也叫引用语义
func (p *Person) SetInfoPointer(n string, s byte, a int) {
	p.name = n
	p.sex = s
	p.age = a
	fmt.Println("p = ", p)
	fmt.Printf("SetInfoPointer p = %p\n", p)
}

func main() {
	s1 := Person{"go", 'm', 12}
	fmt.Printf("&s1 = %p\n", &s1) //打印s1的地址

	//值语义验证,首先调用上边值语义定义的方法进行重新赋值,然后对比两者的地址以及值
	// s1.SetInfoValue("mike", 'b', 18)
	// fmt.Println("s1 = ", s1) //打印内容,地址以及值都不一样

	//引用语义验证
	(&s1).SetInfoPointer("mike", 'b', 18)
	fmt.Println("s1 = ", s1) //打印内容,地址以及值都一样
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

# 5,方法集。

类型的方法集是指可以被该类型的值调用的所有方法的集合。

用实例 value 和 pointer 调用方法(含匿名字段)不受方法集约束,编译器总是查找全部方法,并自动转换 receiver 实参。

# 1,指针类型方法集。

一个纸箱自定义类型的值的指针,它的方法集由该类型定义的所有方法组成,无论这些方法接受的是一个值还是一个指针。

如果在指针上电调用一个接受值的方法,Go 语言会聪明地将该指针解引用,并将指针所指的底层值作为方法的接收者。

package main

import "fmt"

type Person struct {
	name string
	sex  byte
	age  int
}

func (p Person) SetInfoValue() {
	fmt.Println("SetInfoValue")
}

func (p *Person) SetInfoPointer() {
	fmt.Println("SetInfoPointer")
}

func main() {
	//结构体变量是一个指针变量,它能够调用哪些方法,那么这些方法就是一个集合,简称方法集
	p := &Person{"mike", 'm', 18}
	p.SetInfoPointer()    //func (p Person) SetInfoValue()
	(*p).SetInfoPointer() //把(*p)转换成p后再调用,等价于上面

	//内部做的转换,先把指针p转换成*p后再调用
	(*p).SetInfoValue()
	p.SetInfoValue()
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# 2,普通类型方法集。

package main

import "fmt"

type Person struct {
	name string
	sex  byte
	age  int
}

func (p Person) SetInfoValue() {
	fmt.Println("SetInfoValue")
}

func (p *Person) SetInfoPointer() {
	fmt.Println("SetInfoPointer")
}

func main() {
	p := Person{"mike", 'm', 18}
	p.SetInfoPointer() //func (p Person) SetInfoValue()
	//内部先把p转换成(*p)后再调用
	p.SetInfoValue()
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

说白了,所谓方法集就是 Go 语言内置了智能的类型匹配功能,也就是说,当我们需要调用某个类型的函数的时候,可以使用通用的某一个方式,编译器会自动进行匹配识别。

# 6,匿名字段。

# 1,方法的继承。

package main

import "fmt"

type Person struct {
	name string //名字
	sex  byte   //性别,字符类型
	age  int    //年龄
}

//Person类型,实现了一个方法
func (tmp *Person) PrintInfo() {
	fmt.Printf("name=%s,sex=%c,age=%d\n", tmp.name, tmp.sex, tmp.age)
}

//有个学生,继承Person字段,成员和方法都继承了
type Student struct {
	Person //匿名字段
	id     int
	addr   string
}

func main() {
	s := Student{Person{"erya", 'm', 12}, 123, "ny"}
	s.PrintInfo()
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 2,方法的重写。

package main

import "fmt"

type Person struct {
	name string //名字
	sex  byte   //性别,字符类型
	age  int    //年龄
}

//Person类型,实现了一个方法
func (tmp *Person) PrintInfo() {
	fmt.Printf("name=%s,sex=%c,age=%d\n", tmp.name, tmp.sex, tmp.age)
}

//有个学生,继承Person字段,成员和方法都继承了
type Student struct {
	Person //匿名字段
	id     int
	addr   string
}

//Student 也实现了一个方法,这个方法和Person方法同名,这种方法叫重写
func (tmp *Student) PrintInfo() {
	fmt.Println("Student : tmp=", tmp)
}

func main() {
	s := Student{Person{"erya", 'm', 12}, 123, "ny"}
	//就近原则:先找本作用域的方法,找不到再用继承的方法
	s.PrintInfo() //到底是调用的是Person,还是Student呢?答案是Student

	//继承的方法可以通过显式调用
	s.Person.PrintInfo()
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

# 3,方法值。

package main

import "fmt"

type Person struct {
	name string
	sex  byte
	age  int
}

func (p Person) SetInfoValue() {
	fmt.Printf("SetInfoValue: %p,%v\n", &p, p)
}

func (p *Person) SetInfoPointer() {
	fmt.Printf("SetInfoPointer: %p,%v\n", p, p)
}

func main() {
	p := Person{"erya", 'm', 12}
	fmt.Printf("SetInfoPointer: %p,%v\n", &p, p)

	p.SetInfoPointer() //传统调用方式

	//保存函数入口地址
	pFunc := p.SetInfoPointer //这个就是方法值,调用函数时,无需再传递接收者,隐藏了接受者
	pFunc()

	vFunc := p.SetInfoValue
	vFunc()

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

# 4,方法表达式。

package main

import "fmt"

type Person struct {
	name string
	sex  byte
	age  int
}

func (p Person) SetInfoValue() {
	fmt.Printf("SetInfoValue: %p,%v\n", &p, p)
}

func (p *Person) SetInfoPointer() {
	fmt.Printf("SetInfoPointer: %p,%v\n", p, p)
}

func main() {
	p := Person{"erya", 'm', 12}
	fmt.Printf("SetInfoPointer: %p,%v\n", &p, p)

	//方法值 f := p.SetInfoPointer 隐藏了接收者
	//方法表达式
	f := (*Person).SetInfoPointer
	f(&p) //显式把接收者传递过去 ===》 p.SetInfoPointer()

	f2 := (Person).SetInfoValue
	f2(p) //显式把接收者传递过去 ===》 p.SetInfoValue()

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
微信 支付宝
#go
上次更新: 2024/06/13, 22:13:45
匿名组合
接口

← 匿名组合 接口→

最近更新
01
记录二五年五一之短暂回归家庭
05-09
02
学习周刊-总第210期-2025年第19周
05-09
03
学习周刊-总第209期-2025年第18周
05-03
更多文章>
Theme by Vdoing | Copyright © 2017-2025 | 点击查看十年之约 | 浙ICP备18057030号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式