Golang接收者方法语法糖
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 中,当一个变量是可变的(也就是说,该变量是一个具有地址的变量,我们可以对其进行寻址操作),我们可以通过对该变量的指针进行方法调用来执行对该变量的操作,否则就会导致编译错误。
参考:Go 中的那些语法糖
参考:Go 挖坑指南: cannot take the address & cannot call pointer method
Golang接收者方法语法糖的更多相关文章
- C#语法糖之第五篇: 泛型委托- Action<T>
因为工作的原因(其实还是个人的惰性)昨天没有给大家分享文章,然后这几天也有很多园友也提出了他们报告的意见及指导,再次感谢这些兄弟们的照顾我 和支持,这个分类的文章我当时想的是把我的学习经验和工作中用到 ...
- 【Unity3D游戏开发】之利用语法糖添加自定义拓展方法(下) (十八)
首先需要声明的是“语法糖”这个词绝非贬义词,它可以给我带来方便,是一种便捷的写法,编译器会帮我们做转换:而且可以提高开发编码的效率,在性能上也不会带来损失.这让java开发人员羡慕不已,呵呵. 1. ...
- C#语法糖之第四篇: 扩展方法
今天继续分享C#4.0语法糖的扩展方法,这个方法也是我本人比较喜欢的方法.大家先想想比如我们以前写的原始类型不能满足现在的需求,而需要在该类型中添加新的方法来实现时大家会怎么做.我先说一下我没有学习到 ...
- C#语法糖: 扩展方法(常用)
今天继续分享C#4.0语法糖的扩展方法,这个方法也是我本人比较喜欢的方法.大家先想想比如我们以前写的原始类型不能满足现在的需求,而需要在该类型中添加新的方法来实现时大家会怎么做.我先说一下我没有学习到 ...
- python进阶之内置函数和语法糖触发魔法方法
前言 前面已经总结了关键字.运算符与魔法方法的对应关系,下面总结python内置函数对应的魔法方法. 魔法方法 数学计算 abs(args):返回绝对值,调用__abs__; round(args): ...
- vue2使用组件进行父子互相传值的sync语法糖方法和原生方法
原生方法:(事件名可以不在props通道里) 子类通过props通道绑定父类里data里的jjjjjj(@:fefefeff='jjjjjjjjjjjjj') 父组件通过监听fefeff事件来把子类传 ...
- golang 赋值与声明语法糖使用注意事项
赋值与声明语法糖 基本用法略, 搜索即可 注意事项 类型推断 := 会自动进行类型推断, 当想要的类型不是自己想要的类型时需要进行类型转换 // i1 默认是 int 类型 i1 := 1 // 当需 ...
- golang指针接收者和值接收者方法调用笔记
初学go时很多同学会把 值接收者 和 指针接收者 的方法相互调用搞混淆,好多同学都只记得指针类型可以调用值接收者方法和指针接收者方法,而值类型只能调用值接收者方法,其实不然,在某些情况下,值类型也是可 ...
- 探索C#之6.0语法糖剖析
阅读目录: 自动属性默认初始化 自动只读属性默认初始化 表达式为主体的函数 表达式为主体的属性(赋值) 静态类导入 Null条件运算符 字符串格式化 索引初始化 异常过滤器when catch和fin ...
- C#语法糖大汇总
首先需要声明的是"语法糖"这个词绝非贬义词,它可以给我带来方便,是一种便捷的写法,编译器会帮我们做转换:而且可以提高开发编码的效率,在性能上也不会带来损失.这让java开发人员羡慕 ...
随机推荐
- 根据已知经纬度导出对应地点的NDVI值
首先把经纬度和NDVI投影到同一个坐标上,其次把excel的经纬度到出城shp格式 然后执行如下步骤: [工具][spatial analyst 工具][提取分析][多值提取至点]
- Java笔记第十弹
网络编程三要素 1.IP地址--设备的标识 2.端口--应用程序的标识 3.协议--常见的协议有UDP协议和TCP协议 IP地址 1.IPv4(二进制数据) 2.IPv6 常用命令: ipconfig ...
- 加深印象篇之Servlet
环境配置 需要配置web.xml文件, <web-app> <servlet> <servlet-name>你起的一个类的名字</servlet-name&g ...
- Why WebRTC|“浅入深出”的工作原理详解
前言 近几年实时音视频通信应用呈现出了大爆发的趋势.在这些实时通信技术的背后,有一项不得不提的技术--WebRTC. 今年 1 月,WebRTC 被 W3C 和 IETF 发布为正式标准.据调研机构 ...
- IDEA-日志管理神器
Grep Console-插件的好处就在于能使控制台输出日志时,可以直接修改插件中定义好的规则,也可以根据自己定义的规则,输出不同的颜色.这样就可以将错误信息标记成显眼的颜色,方便查看,提高bug寻找 ...
- mybatisplus数据层标准开发---分页功能
1.创建一个拦截器类 package com.itheima.config; import com.baomidou.mybatisplus.extension.plugins.MybatisPlus ...
- Java面试——缓存
一.什么是缓存 [1]缓存就是数据交换的缓冲区(称作:Cache),当某一硬件要读取数据时,会首先从缓存中查询数据,有则直接执行,不存在时从磁盘中获取.由于缓存的数据比磁盘快的多,所以缓存的作用就是帮 ...
- BrightlyPro - 照片视屏后期自动调色增亮工具
BrightlyPro 自动增强您的照片和视频,你最需要它.通过一个滑块,BrightlyPro 立即使您的照片自适应地亮起来,同时保持光影之间令人愉悦的平衡,而不会扭曲已经点亮的区域. 下载 ➤ B ...
- 重打包APK绕过签名校验
这里先提一种针对性校强但简单好理解的办法,纯Java实现,代码大概也就50行不到吧. 还有更强的并且能过各种保护(反调试反HOOK反内存修改等等)的万能方法,不过较复杂,长篇大论的,等有空整理出来再提 ...
- [IDE]IntelliJ IDEA 不能识别 Java 项目 [转]
本文转载自 IntelliJ IDEA 不能识别 Java 项目 - 博客园/SmartJuneThx 解决方法 非maven项目 在 src 目录上点右键,选择 Mark Directory As ...