Go 结构体 匿名字段


结构体 字段只有类型 没有名 这样的字段称为匿名字段 Anonymous Field
type Person struct {
    string //这里 匿名字段没有名称 但默认 其类型string  为匿名字段的名称 即string
    int
}
func main() {  
    p := Person{"Naveen", 50}
    fmt.Println(p)
    //结构体Person  字段是匿名的 Go 默认 字段名是 各自的类型  Person 结构体有两个字段名 分别为 string 和 int
    var p1 Person
    p1.string = "Karl Marx "
    p1.int = 22
    fmt.Println(p1)
}//output
{Naveen 50}
{Karl Marx  22}

如果有多个匿名字段的类型是一致的  
type Person struct {
    string
    int
    string
}
func main() {}
报错  duplicate field string
结构体嵌入和匿名成员
Go语言 结构体嵌入 机制 让结构体包含另一个结构体类型的 匿名成员
通过 点运算符 x.f 访问匿名成员链中 嵌套的x.d.e.f成员
匿名成员的数据类型必须是命名的(而不是匿名)类型 或指向一个命名的类型的指针
type Circle struct {
    Point
    Radius int
}
type Wheel struct {
    Circle
    Spokes int
}
有了匿名嵌入的特性 可直接访问内嵌类型的成员变量而不需要给出完整的路径
var w Wheel
w.X = 8 // 等价于 w.Circle.Point.X = 8
w.Y = 8 // 等价于 w.Circle.Point.Y = 8
w.Radius = 5 // 等价于 w.Circle.Radius = 5
w.Spokes = 20
内嵌类型的方法也会提升为外部类型的方法

匿名组合不是继承 方法的接受者没变

当嵌入一个类型 这个类型的方法就变成了外部类型的方法 但是当它被调用时 方法的接受者是内部类型(嵌入类型) 而非外部类型  Effective Go
type Job struct {
    Command string
    *log.Logger
}
func (job *Job)Start() {
    job.Log("starting now...")
    ... //do something
    job.Log("started.")
}
Job 即使组合后调用的方式变成了job.Log(…) 但Log函数的接收者仍然是 log.Logger 指针
因此在Log中 不可能访问到job的其他成员方法和变量

内嵌类型不是基类

将内嵌类型 看作一个基类 外部类型看作其子类或者继承类 或将外部类型看作 "is a" 内嵌类型 是错误的
type Point struct{ X, Y float64 }
type ColoredPoint struct {
    Point
    Color color.RGBA
}
func (p Point) Distance(q Point) float64 {
    dX := q.X - p.X
    dY := q.Y - p.Y
    return math.Sqrt(dX*dX + dY*dY)
}
对Distance方法的调用  Distance 参数 Point类型 但q并不是一个Point类 所以尽管q有着Point这个内嵌类型 也必须要显式地选择它
尝试直接传q 会看到错误
red := color.RGBA{255, 0, 0, 255}
blue := color.RGBA{0, 0, 255, 255}
var p = ColoredPoint{Point{1, 1}, red}
var q = ColoredPoint{Point{5, 4}, blue}
fmt.Println(p.Distance(q.Point)) // "5"
p.Distance(q) // compile error: cannot use q (ColoredPoint) as Point
ColoredPoint 并不是一个 Point 但ColoredPoint "has a" Point 且它有从Point类里引入的 Distance方法

从实现的角度来考虑问题 内嵌字段 指导编译器 生成额外的包装方法 来委托已经声明好的方法 和下面的形式是等价的
func (p ColoredPoint) Distance(q Point) float64 {
    return p.Point.Distance(q)
}
当Point.Distance 被以上编译器生成的包装方法调用时 它的接收器值是p.Point 而不是p

匿名冲突duplicate field 和隐式名字

匿名成员有隐式的名字 以其类型名称(去掉包名部分)作为成员变量的名字
同一级同时包含两个类型相同的匿名成员 会导致名字冲突
type Logger struct {
    Level int
}
type MyJob struct {
    *Logger
    Name string
    *log.Logger // duplicate field Logger
}
说明匿名组合不是继承
匿名成员有隐式的名字
匿名可能冲突(duplicate field)
package main
 import (
    "fmt"
)
type A struct {
    B
    m int
}
type B struct {
    n int
}
func (b *B) my_print() {
    fmt.Printf("print B.n=%d\n", b.n)
}
func main() {
    a := &A{B{2}, 3}
    fmt.Printf("A.n=%d, A.m=%d\n", a.n, a.m)
    a.my_print()
}//output
A.n=2, A.m=3
print B.n=2
B是结构体A的匿名字段 所以B的成员n 对于结构体A来说 是A自己的成员变量a.n
B的方法也是A的方法  a.my_print()