Golang中接收者方法语法糖的使用方法详解
作者:人艰不拆_zmc 发布时间:2024-05-21 10:26:49
1、概述
在《Golang常用语法糖》这篇博文中我们讲解Golang中常用的12种语法糖,在本文我们主要讲解下接收者方法语法糖。
在介绍Golang接收者方法语法糖前,先简单说下Go 语言的指针 (Pointer),大致上理解如下:
变量名前的 & 符号,是取变量的内存地址,不是取值;
数据类型前的 * 符号,代表要储存的是对应数据类型的内存地址,不是存值;
变量名前的 * 符号,代表从内存地址中取值 (Dereferencing)。
注意 1:golang 指针详细介绍请参见《Golang指针隐式间接引用》此篇博文。
2、接收者方法语法糖
在 Go 中,对于自定义类型 T,为它定义方法时,其接收者可以是类型 T 本身,也可能是 T 类型的指针 *T。
type Instance struct{}
func (ins *Instance) Foo() string {
return ""
}
在上例中,我们定义了 Instance 的 Foo 方法时,其接收者是一个指针类型(*Instance)。
func main() {
var _ = Instance{}.Foo() //编译错误:cannot call pointer method on Instance{} ,变量是不可变的(该变量没有地址,不能对其进行寻址操作)
}
因此,如果我们用 Instance 类型本身 Instance{} 值去调用 Foo 方法,将会得到以上错误。
type Instance struct{}
func (ins Instance) Foo() string {
return ""
}
func main() {
var _ = Instance{}.Foo() // 编译通过
}
此时,如果我们将 Foo 方法的接收者改为 Instance 类型,就没有问题。
这说明,定义类型 T 的函数方法时,其接收者类型决定了之后什么样的类型对象能去调用该函数方法。但,实际上真的是这样吗?
type Instance struct{}
func (ins *Instance) String() string {
return ""
}
func main() {
var ins Instance
_ = ins.String() // 编译器会自动获取 ins 的地址并将其转换为指向 Instance 类型的指针_ = (&ins).String()
}
实际上,即使是我们在实现 Foo 方法时的接收者是指针类型,上面 ins 调用的使用依然没有问题。
Ins 值属于 Instance 类型,而非 *Instance,却能调用 Foo 方法,这是为什么呢?这其实就是 Go 编译器提供的语法糖!
当一个变量可变时(也就是说,该变量是一个具有地址的变量,我们可以对其进行寻址操作),我们对类型 T 的变量直接调用 *T 方法是合法的,因为 Go 编译器隐式地获取了它的地址。变量可变意味着变量可寻址,因此,上文提到的 Instance{}.Foo() 会得到编译错误,就在于 Instance{} 值不能寻址。
注意 1:在 Go 中,即使变量没有被显式初始化,编译器仍会为其分配内存空间,因此变量仍然具有内存地址。不过,由于变量没有被初始化,它们在分配后仅被赋予其类型的默认零值,而不是初始值。当然,这些默认值也是存储在变量分配的内存空间中的。
例如,下面的代码定义了一个整型变量 x,它没有被显式初始化,但是在分配内存时仍然具有一个地址:
var x int
fmt.Printf("%p\n", &x) // 输出变量 x 的内存地址
输出结果类似于:0xc0000120a0,表明变量 x 的内存地址已经被分配了。但是由于变量没有被初始化,x 的值将为整型的默认值 0。
3、深入测试
3.1 示例
package main
type B struct {
Id int
}
func New() B {
return B{}
}
func New2() *B {
return &B{}
}
func (b *B) Hello() {
return
}
func (b B) World() {
return
}
func main() {
// 方法的 * 为 *T 类型
New().Hello() // 编译不通过
b1 := New()
b1.Hello() // 编译通过
b2 := B{}
b2.Hello() // 编译通过
(B{}).Hello() // 编译不通过
B{}.Hello() // 编译不通过
New2().Hello() // 编译通过
b3 := New2()
b3.Hello() // 编译通过
b4 := &B{} // 编译通过
b4.Hello() // 编译通过
(&B{}).Hello() // 编译通过
// 方法的 * 为 T 类型
New().World() // 编译通过
b5 := New()
b5.World() // 编译通过
b6 := B{}
b6.World() // 编译通过
(B{}).World() // 编译通过
B{}.World() // 编译通过
New2().World() // 编译通过
b7 := New2()
b7.World() // 编译通过
b8 := &B{} // 编译通过
b8.World() // 编译通过
(&B{}).World() // 编译通过
}
输出结果:
./main.go:25:10: cannot call pointer method on New()
./main.go:25:10: cannot take the address of New()
./main.go:33:10: cannot call pointer method on B literal
./main.go:33:10: cannot take the address of B literal
./main.go:34:8: cannot call pointer method on B literal
./main.go:34:8: cannot take the address of B literal
3.2 问题总结
假设 T 类型的方法上 * 既有 T 类型的,又有 *T 指针类型的,那么就不可以在不能寻址的 T 值上调用 *T * 的方法
&B{} 是指针,可寻址
B{} 是值,不可寻址
b := B{} b是变量,可寻址
4、总结
在 Golang 中,当一个变量是可变的(也就是说,该变量是一个具有地址的变量,我们可以对其进行寻址操作),我们可以通过对该变量的指针进行方法调用来执行对该变量的操作,否则就会导致编译错误。
来源:https://www.cnblogs.com/zhangmingcheng/p/17395367.html


猜你喜欢
- <input id="workname" style="width: 200px" name=
- <?phpfunction map($fun, $list,$params=array()){ $
- django-admin基本介绍Django 提供了基于 web 的管理工具。Django 自动管理工具是 django.contrib 的
- 本文实例讲述了PHP调用全国天气预报数据接口查询天气。分享给大家供大家参考,具体如下:基于PHP的聚合数据全国天气预报API服务请求的代码样
- 关于算法的学习,小编觉得编程语言中的算法大都有一些相通的地方,主要的方面一是了解这一算法能用来干什么,另一方面,学习它在这类编程语言中怎么实
- 处理多个数据和多文件时,使用for循环的速度非常慢,此时需要用多线程来加速运行进度,常用的模块为multiprocess和joblib,下面
- defaultdict 主要用来需要对 value 做初始化的情形。对于字典来说,key 必须是 hashable,immutable,un
- 单测框架的作用测试发现:从多个文件中寻找测试用例。测试执行:按照一定顺序去执行并且生成结果。测试断言:判断最终结果与实际结果的差异。测试报告
- Error是Go语言开发中最基础也是最重要的部分,跟其他语言的try catch的作用基本一致,想想在PHP JAVA开发中,try cat
- 需求是, 同一个页面, 有多组(不固定), 每组区块数量不一定一样的小区块. 要求每次只展开一个区块. 实现原理其实很简单, 点击导航, 若
- 一、Go语言中Goroutine的基本原理Go语言里的并发指的是能让某个函数独立于其他函数运行的能力。Go语言的goroutine是一个独立
- MASK图像掩膜处理在图像操作中有时候会用到掩膜处理,如果使用遍历法掩膜图像ROI区域对于python来讲是很慢的,所以我们要找到一种比较好
- 1.whl包whl格式本质上是一个压缩包,里面包含了py文件,以及经过编译的pyd文件。使得可以在不具备编译环境的情况下,选择适合自己的py
- 目录1. 单变量 :=2. 多变量 :=3. 小结:=??Go 语言中 = 和 := 有什么区别1. 单变量 :=Go 语言中新增了一个特殊
- 1. 引用css。这可能是最常见的做法了,对一些特定的元素定义特定的样式。那么使用它,你需要在HTML 页面中加入<link
- 目录1. 序列2. 列表2.1 列表的特性2.1.1 列表的连接操作符和重复操作符2.1.3 列表的索引2.1.4 列表的切片2.1.5 列
- li {list-style-image: url(images/disc.gif);}li的这个样式定义是将列表项目使用图片来代替显示的小
- ASP+javascript实现可显示和隐藏的树型菜单实例:<script language=&qu
- 对str类型数据进行split操作如下:>>> s = 'abc\ndef'>>> s.
- /** * 截取字符串 len为字节长度 * @param str * @param len * @return * @throws Uns