考虑一个二维的绘图程序,提供了一个各类图形的库,例如矩形、椭圆形、星形和轮形等几 何形状。这里是其中两个的定义编程

type    Circle struct {                
X,  Y,  Radius int 
}
type    Wheel struct {                
X, Y,    Radius, Spokes    int
}

一个Circle表明的圆形类型包含了标准圆心的X和Y坐标信息,和一个Radius表示的半径信 息。一个Wheel轮形除了包含Circle类型全部的所有成员外,还增长了Spokes表示径向辐条的 数量。咱们能够这样建立一个wheel变量:函数

var w Wheel 
w.X = 8 
w.Y = 8 
w.Radius = 5 
w.Spokes = 20

随着库中几何形状数量的增多,咱们必定会注意到它们之间的类似和重复之处,因此咱们可 能为了便于维护而将相同的属性独立出来:ui

type Point struct{
     x,y int  
}

type Circle struct{
    Center Point
    Radius int
}

type Wheel struct{
      Circle Ciecle
      Spokes int
}

这样改动以后结构体类型变的清晰了,可是这种修改同时也致使了访问每一个成员变得繁琐:spa

    var    w Wheel
    w.Circle.Center.X = 8 
    w.Circle.Center.Y = 8
    w.Circle.Radius = 5
    w.Spokes = 20

Go语言有一个特性让咱们只声明一个成员对应的数据类型而不指名成员的名字;这类成员就 叫匿名成员。匿名成员的数据类型必须是命名的类型或指向一个命名的类型的指针。下面的 代码中,Circle和Wheel各自都有一个匿名成员。咱们能够说Point类型被嵌入到了Circle结构 体,同时Circle类型被嵌入到了Wheel结构体。指针

type Circle    struct {
    Point 
    Radius    int 
}
type Wheel struct { 
    Circle                
    Spokes    int 
}

得益于匿名嵌入的特性,咱们能够直接访问叶子属性而不须要给出完整的路径:code

var    w Wheel 
w.X = 8    //    equivalent    to    w.Circle.Point.X    =    8 
w.Y = 8        //    equivalent    to    w.Circle.Point.Y    =    8 
w.Radius = 5 //    equivalent    to    w.Circle.Radius    =    5 
w.Spokes = 20

在右边的注释中给出的显式形式访问这些叶子成员的语法依然有效,所以匿名成员并非真 的没法访问了。其中匿名成员Circle和Point都有本身的名字——就是命名的类型名字——可是 这些名字在点操做符中是可选的。咱们在访问子成员的时候能够忽略任何匿名成员部分。
不幸的是,结构体字面值并无简短表示匿名成员的语法, 所以下面的语句都不能编译通 过:对象

w = Wheel{8, 8, 5, 20}  //compile error: unknown fields 
w = Wheel{X:8, Y:8, Radius:5, Spokers:20} //compile error: unknown fields
w = Wheel{Circle{Point{8,8}, 5},20}
w =    Wheel{ Circle:    Circle{ 
                            Point: Point{X:8,Y:8}, 
                            Radius: 5, 
                        }, 
                        Spokes: 20,    //    NOTE:    trailing    comma    necessary    here    (and    at    Radius) 
        }
fmt.Printf("%#v\n",w) 
//    Output: 
//    Wheel{Circle:Circle{Point:Point{X:8,    Y:8},    Radius:5},    Spokes:20}

w.X = 42

fmt.Printf("%#v\n", w) 

//    Output: 
//    Wheel{Circle:Circle{Point:Point{X:42,    Y:8},    Radius:5},    Spokes:20}

须要注意的是Printf函数中%v参数包含的#副词,它表示用和Go语言相似的语法打印值。对于 结构体类型来讲,将包含每一个成员的名字。blog

由于匿名成员也有一个隐式的名字,所以不能同时包含两个类型相同的匿名成员,这会致使 名字冲突。同时,由于成员的名字是由其类型隐式地决定的,全部匿名成员也有可见性的规 则约束。在上面的例子中,Point和Circle匿名成员都是导出的。即便它们不导出(好比改为小 写字母开头的point和circle),咱们依然能够用简短形式访问匿名成员嵌套的成员ci

w.X = 8    //    equivalent    to w.circle.point.X = 8

可是在包外部,由于circle和point没有导出不能访问它们的成员,所以简短的匿名成员访问语 法也是禁止的。编译

到目前为止,咱们看到匿名成员特性只是对访问嵌套成员的点运算符提供了简短的语法糖。 稍后,咱们将会看到匿名成员并不要求是结构体类型;其实任何命名的类型均可以做为结构 体的匿名成员。可是为何要嵌入一个没有任何子成员类型的匿名成员类型呢?
答案是匿名类型的方法集。简短的点运算符语法能够用于选择匿名成员嵌套的成员,也能够 用于访问它们的方法。实际上,外层的结构体不单单是得到了匿名成员类型的全部成员,而 且也得到了该类型导出的所有的方法这个机制能够用于将一个有简单行为的对象组合成有 复杂行为的对象。组合是Go语言中面向对象编程的核心。