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和ScaleB
y⽅法。如果你喜欢从实现的⾓度来考虑问题,内嵌字段会指导编译器去⽣成额外的包装⽅法来委托已经声明好的⽅法,和下⾯的形式是等价的:
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 的任何字段的。
编译器查⽅法的顺序:
1. 先在本结构体定义的⽅法中
2. 不到,再去⼦结构体,然后⼀直递归向下
如果选择器有⼆义性的话编译器会报错,⽐如你有⼀个字段觉Distance,然后⼀个⽅法也叫Distance,那么选择器就不知道你想使⽤的是哪个,编译器就会报错;
匿名结构体
golang语法匿名结构体就是在嵌⼊时,不指定名称,这样⼦会将匿名结果体的所有⽅法引⼊到该类型中;这样在使⽤时有很多便利:例如下⾯对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⽅法也就都被引⼊到了这个匿名结构中了,这让我们能够以⼀个简单明了的语法来对其进⾏加锁解锁操作。