Golang理解-匿名结构体
结构体嵌套
go中使用结构体嵌套来扩展类型
嵌入到结构体中的字段,完全可以当作自己是自己的字段
import "image/color"
type Point struct{ X, Y float64 }
type ColoredPoint struct {
Point
Color color.RGBA
}
ColoredPoint嵌套了Point结构体,从而ColoredPoint就拥有了Point的字段X,Y。 可以直接通过"."操作符来访问;
如果Point拥有自己的方法,那么ColoredPoint也拥有这些方法,而不需要在自己定义
用这种方式,内嵌可以使我们定义字段特别多的复杂类型,我们可以将字段先按小类型分组,然后定义小类型的方法,之后再把它们组合起来。
读者如果对基于类来实现面向对象的语言比较熟悉的话,可能会倾向于将Point看作一个基类,而ColoredPoint看作其子类或者继承类,或者将ColoredPoint看作"is a" Point类型。
但这是错误的理解
。请注意上面例子中对Distance方法的调用。Distance有一个参数是Point类型,但q并不是一个Point类,所以尽管q有着Point这个内嵌类型,我们也必须要显式地选择它。尝试直接传q的话你会看到下面这样的错误:---go语言圣经
var p = ColoredPoint{Point{1, 1}, red}
var q = ColoredPoint{Point{5, 4}, blue}
fmt.Println(p.Distance(q.Point)) // "10"
p.Distance(q) // compile error: cannot use q (ColoredPoint) as Point
一个ColoredPoint并不是一个Point,但他"has a" Point,并且它有从Point类里引入的Distance和ScaleBy方法。如果你喜欢从实现的角度来考虑问题,内嵌字段会指导编译器去生成额外的包装方法来委托已经声明好的方法,和下面的形式是等价的:
func (p ColoredPoint) Distance(q Point) float64 {
return p.Point.Distance(q)
}
func (p *ColoredPoint) ScaleBy(factor float64) {
p.Point.ScaleBy(factor)
}
当Point.Distance被第一个包装方法调用时,它的接收器值是p.Point,而不是p,当然了,在Point类的方法里,你是访问不到ColoredPoint的任何字段的。
编译器查找方法的顺序:
- 先在本结构体定义的方法中找
- 找不到,再去子结构体找,然后一直递归向下找
如果选择器有二义性的话编译器会报错,比如你有一个字段觉Distance,然后一个方法也叫Distance,那么选择器就不知道你想使用的是哪个,编译器就会报错;
匿名结构体
匿名结构体就是在嵌入时,不指定名称,这样子会将匿名结果体的所有方法引入到该类型中;这样在使用时有很多便利:例如下面对map的操作;
我们知道map时非线程安全的,会存在读或写竞争,我们需要在对map的操作时进行加锁;
package main
// 匿名结构提嵌套,可以引入匿名结构体所有的方法
import (
"fmt"
"sync"
)
type safeMap struct {
sync.Mutex
mapping map[string]string
}
// 获取map中元素的值
func (m safeMap) Get(key string) (value string) {
// 引入了sync.Mutex的加锁和解锁方法
m.Lock()
value = m.mapping[key]
m.Unlock()
return
}
func main() {
m := map[string]string{
"a" : "alpha",
"i" : "integer",
}
cache := safeMap{
mapping: m,
}
fmt.Println(cache.Get("a"))
fmt.Println(cache.Get("i"))
}
因为sync.Mutex字段也被嵌入到了这个struct里,其Lock和Unlock方法也就都被引入到了这个匿名结构中了,这让我们能够以一个简单明了的语法来对其进行加锁解锁操作。