golang 結構體嵌入和匿名成員

考慮一個二維的繪圖程序,提供了一個各類圖形的庫,例如矩形、橢圓形、星形和輪形等幾 何形狀。這裏是其中兩個的定義編程

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語言中面向對象編程的核心。

相關文章
相關標籤/搜索