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

隨着庫中幾何形狀數量的增多,我們一定會注意到它們之間的相似和重復之處,所以我們可 能為了便於維護而將相同的屬性獨立出來:

type Point struct{
     x,y int  
}

type Circle struct{
    Center Point
    Radius int
}

type Wheel struct{
      Circle Ciecle
      Spokes int
}

這樣改動之后結構體類型變的清晰了,但是這種修改同時也導致了訪問每個成員變得繁瑣:

    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 
}

得益於匿名嵌入的特性,我們可以直接訪問葉子屬性而不需要給出完整的路徑:

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語言類似的語法打印值。對於 結構體類型來說,將包含每個成員的名字。

因為匿名成員也有一個隱式的名字,因此不能同時包含兩個類型相同的匿名成員,這會導致 名字沖突。同時,因為成員的名字是由其類型隱式地決定的,所有匿名成員也有可見性的規 則約束。在上面的例子中,Point和Circle匿名成員都是導出的。即使它們不導出(比如改成小 寫字母開頭的point和circle),我們依然可以用簡短形式訪問匿名成員嵌套的成員

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

但是在包外部,因為circle和point沒有導出不能訪問它們的成員,因此簡短的匿名成員訪問語 法也是禁止的。

到目前為止,我們看到匿名成員特性只是對訪問嵌套成員的點運算符提供了簡短的語法糖。 稍后,我們將會看到匿名成員並不要求是結構體類型;其實任何命名的類型都可以作為結構 體的匿名成員。但是為什么要嵌入一個沒有任何子成員類型的匿名成員類型呢?
答案是匿名類型的方法集。簡短的點運算符語法可以用於選擇匿名成員嵌套的成員,也可以 用於訪問它們的方法。實際上,外層的結構體不僅僅是獲得了匿名成員類型的所有成員,而 且也獲得了該類型導出的全部的方法這個機制可以用於將一個有簡單行為的對象組合成有 復雜行為的對象。組合是Go語言中面向對象編程的核心。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM