当前位置:网站首页>Golang structs & methods
Golang structs & methods
2022-08-03 13:14:00 【Cloud full of notes】
1. Golang 结构体&方法
对于这一章内容,"匿名字段"用的非常多,它是其声明中只有类型而没有名称的字段,可以以一种很自然的方式为被嵌入的类型带来新的属性和能力.不过,我们需要小心可能产生"屏蔽"现象的地方,尤其是当存在多个嵌入字段或者多层嵌入的时候,"屏蔽"现象可能会让你的实际引用与你的预期不符.另外,你一定要梳理清楚值方法和指针方法的不同之处,包括这两种方法各自能做什么、不能做什么以及会影响到其所属类型的哪些方面.这涉及值的修改、方法集合和接口实现.
1.1. 匿名字段
所谓匿名字段,是指没有名字,仅有类型的字段,被称为嵌入字段或者嵌入类型.
type attr struct {
perm int
}
type file struct {
name string
attr // 仅有类名
}
func main() {
var f file
f.name = "lvmenglou"
f.perm = 24 // 等价于 f.attr.perm = 24
}
1.2. 方法覆盖
方法存在同名遮蔽问题,通过匿名字段 user
构建 manager
结构,两者都实现了 toString()
方法,对于变量 m
调用 toString()
方法时,会直接屏蔽掉 user.toString()
方法,实现覆盖操作;但是也可以通过 m.user.toString()
直接访问的 user
的 toString()
方法.
type user struct{
}
type manager struct {
user // 匿名字段
}
func (user) toString() string {
return "user"
}
func (m manager) toString() string {
return m.user.toString() + ";manager"
}
func main() {
var m manager
println(m.toString()) // 输出:user;manager
println(m.user.toString()) // 输出:user
}
1.3. 值方法和指针方法
当你给某个 struct
定制一个字符串转换方法,可能会纠结选择 value methods 还是 pointer methods 方式:
func (ms MyStruct) String() string // value methods
func (ms *MyStruct) String() string // pointer methods
在官方 effective go 文档中,对两者区别描述如下:
- 值方法 (value methods) 可以通过指针和值调用,但是指针方法 (pointer methods) 只能通过指针来调用.
- 但有一个例外,如果某个值是可寻址的 (addressable, 或者说左值), 那么编译器会在值调用指针方法时自动插入取地址符,使得在此情形下看起来像指针方法也可以通过值来调用.
type Foo struct {
name string
}
func (f *Foo) PointerMethod() {
fmt.Println("pointer method on", f.name)
}
func (f Foo) ValueMethod() {
fmt.Println("value method on", f.name)
}
func NewFoo() Foo {
// 返回一个右值,不可寻址
return Foo{
name: "right value struct"}
}
func main() {
f1 := Foo{
name: "value struct"}
f1.PointerMethod() // 编译器会自动插入取地址符,变为 (&f1).PointerMethod()
f1.ValueMethod()
f2 := &Foo{
name: "pointer struct"}
f2.PointerMethod()
f2.ValueMethod() // 编译器会自动解引用,变为 (*f2).PointerMethod()
NewFoo().ValueMethod()
NewFoo().PointerMethod() // Error!!!
}
简而言之,就是不管是普通对象还是指针,都可以调用他们的值方法和指针方法,因为编译器会自行处理,但是对于右值(也就是通过函数返回的临时结构体变量等), 只能调用值方法,不能调用指针方法.(备注:对于左值和右值的区别,最重要区别就是是否可以被寻址,可以被寻址的是左值,既可以出现在赋值号左边也可以出现在右边;不可以被寻址的即为右值,比如函数返回值、字面值、常量值等等,只能出现在赋值号右边.) 对于某个特定场景,两者如何取舍其实和另一个问题等价:就是你在定义函数时如何传参——是传值还是传指针.比如上述例子:
func (f *Foo) PointerMethod() {
fmt.Println("pointer method on ", f.name)
}
func (f Foo) ValueMethod() {
fmt.Println("value method on", f.name)
}
可以转换为下面两个函数进行考虑,用 Go 的术语来说,就是将函数的 receiver 看做是 argument:
func PointerMethod(f *Foo) {
fmt.Println("pointer method on ", f.name)
}
func ValueMethod(f Foo) {
fmt.Println("value method on", f.name)
}
在定义 receiver 为值还是指针时,主要有以下几个考虑点:
- 方法是否需要修改 receiver 本身.如果需要,那 receiver 必然要是指针.
- 效率问题.如果 receiver 是值,那在方法调用时一定会产生 struct 拷贝,而大对象拷贝代价很大.
- 一致性.对于同一个 struct 的方法,value method 和 pointer method 混杂用肯定是优雅.
那啥时候用 value method 呢?很简单的不可变对象使用 value method 可以减轻 gc 负担,貌似也就这些好处了.因此请记住:遇事不决请用 pointer method! ! ! 对于第一条,如果需要修改 receiver 本身,必须用指针,我再举个例子:
type N int
func (n N) value() {
// value method
n++
fmt.Printf("v:%p, %v\n", &n, n)
}
func (n *N) pointer() {
// point method
(*n)++
fmt.Printf("p:%p, %v\n", n, *n)
}
func main() {
var a N = 5
a.value()
a.pointer()
fmt.Printf("a:%p,%v\n", &a, a)
}
输出:
v: 0x8200741c8, 26 // 虽然 n 被打印为 26, 但是在 main() 中,n 的值还是 25, 未修改!! ! p: 0x8200741c0, 26 a: 0x8200741c0, 26 // n 的值被修改为 26, 是通过 a.pointer() 修改的!! !
1.4. 方法集
所谓"方法集", 简单来说,就是对于普通类型和指针类型,所包含的方法集合,我们先看个例子:
type S struct{
}
type T struct {
S // 匿名嵌入字段
}
func (S) sVal() {
}
func (*S) sPtr() {
}
func (T) sVal() {
}
func (*T) sPtr() {
}
func methodSet(a interface{
}) {
t := reflect.TypeOf(a)
for i, n := 0, t.NumMethod(); i < n; i++ {
m := t.Method(i)
fmt.Println(m.Name, m.Type)
}
}
func main() {
var t T
methodSet(t) // 显示 T 方法集
println("---------------")
methodSet(&t) // 显示* T 方法集
}
输出:
sVal func(main.T)tVal func(main.T)----------------sPtr func(*main.T)sVal func(*main.T)tPtr func(*main.T)tVal func(*main.T)
可以看出,普通类型的方法集,是值方法
;指针类型的方法集,是值方法
+ 指针方法
,更多情况可总结如下:
- 类型
T
方法集包含所有的receive T
方法; - 类型
*T
方法集包含所有receive T
+*T
方法; - 匿名嵌入
S
,T
方法集包含所有receive T
方法; - 匿名嵌入
*S
,T
方法集包含所有receive S
+*S
方法; - 匿名嵌入
S
或*S
,*T
方法集包含所有receive S
+*S
方法.
1.5. 总结
对于这一章内容,"匿名字段"用的非常多,它是其声明中只有类型而没有名称的字段,可以以一种很自然的方式为被嵌入的类型带来新的属性和能力.不过,我们需要小心可能产生"屏蔽"现象的地方,尤其是当存在多个嵌入字段或者多层嵌入的时候,"屏蔽"现象可能会让你的实际引用与你的预期不符.另外,你一定要梳理清楚值方法和指针方法的不同之处,包括这两种方法各自能做什么、不能做什么以及会影响到其所属类型的哪些方面.这涉及值的修改、方法集合和接口实现.
再次强调,嵌入字段是实现类型间组合的一种方式,这与继承没有半点儿关系.Go 语言虽然支持面向对象编程,但是根本就没有"继承"这个概念.最后就是"方法集", 对于普通类型和指针类型,当存在匿名字段时,他们的值方法和指针方法的所属关系,这个是需要掌握的,这里我们没有讨论实际的应用场景,但是当涉及到接口实现和类型转换时,如果对这块知识掌握不够的话,转换会非常容易出现问题,后面在讲接口时,这块会再去讲解.对于方法表达式这块,这块知识不容易和其它知识混淆,也不难掌握,大家可以自行查阅相关资料即可.
边栏推荐
- 什么是分布式锁?几种分布式锁分别是怎么实现的?
- 数据库基础知识一(MySQL)[通俗易懂]
- 汉源高科G8032标准ERPS环网交换机千兆4光10电工业以太网交换机环网+WEB管理+SNMP划VLAN
- 来广州找工作有一个多月了,今天终于有着落了,工资7000
- Yahoo!Answers - data set
- An introduction to 3D tools
- 五、函数的调用过程
- [Blue Bridge Cup Trial Question 48] Scratch Dance Machine Game Children's Programming Scratch Blue Bridge Cup Trial Question Explanation
- ECCV 2022|通往数据高效的Transformer目标检测器
- An动画基础之元件的影片剪辑动画与传统补间
猜你喜欢
Graphic animation and button animation of an animation basic component
图像融合DDcGAN学习笔记
An工具介绍之形状工具及渐变变形工具
How to disable software from running in the background in Windows 11?How to prevent apps from running in the background in Windows 11
基于php家具销售管理系统获取(php毕业设计)
[R] Use grafify for statistical plotting, ANOVA, intervention comparisons, and more!
Real number rounding and writing to file (C language file)
【实战技能】单片机bootloader的CANFD,I2C,SPI和串口方式更新APP视频教程(2022-08-01)
Image fusion SDDGAN article learning
HCIP第十五天笔记(企业网的三层架构、VLAN以及VLAN 的配置)
随机推荐
业界新标杆!阿里开源自研高并发编程核心笔记(2022最新版)
Nodejs 安装依赖cpnm时,install 出现Error: Cannot find module ‘fs/promises‘
易观分析:2022年Q2中国网络零售B2C市场交易规模达23444.7亿元
Image fusion SDDGAN article learning
IronOS, an open source system for portable soldering irons, supports a variety of portable DC, QC, PD powered soldering irons, and supports all standard functions of smart soldering irons
setTimeout 、setInterval、requestAnimationFrame
安全自定义 Web 应用程序登录
Win11怎么禁止软件后台运行?Win11系统禁止应用在后台运行的方法
An animation optimization of shape tween and optimization of traditional tweening
实数取整写入文件(C语言文件篇)
IDEA的模板(Templates)
Golang 结构体&方法
【Verilog】HDLBits题解——验证:阅读模拟
Graphic animation and button animation of an animation basic component
Key points for account opening of futures companies
什么是分布式锁?几种分布式锁分别是怎么实现的?
d作者:d的新特性
技术分享 | 接口自动化测试如何搞定 json 响应断言?
Redis 6 的多线程
leetcode16最接近的三数之和 (排序+ 双指针)