Golang中匿名組合實現偽繼承

來源:互聯網
上載者:User

本文同時發佈於個人CSDN部落格:https://blog.csdn.net/ggq89/article/details/82084338

"Go語言的物件導向機制與一般語言不同。 它沒有類階層, 甚至可以說沒有類; 僅僅通過組合( 而不是繼承) 簡單的對象來構建複雜的對象。" -- 《Go語言聖經》

1. 結構體嵌入和匿名成員

Go語言提供別樣的 結構體嵌入 機制,讓一個結構體包含另一個結構體類型的 匿名成員 , 這樣就可以通過簡單的點運算子x.f來訪問匿名成員鏈中嵌套的x.d.e.f成員。

Go語言有一個特性讓我們只聲明一個成員對應的資料類型而不指名成員的名字; 這類成員就叫匿名成員。 匿名成員的資料類型必須是命名的(而不是匿名的)類型或指向一個命名的類型的指標。

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

由於有了匿名嵌入的特性, 我們可以直接存取內嵌類型的成員變數而不需要給出完整的路徑:

var w Wheelw.X = 8 // 等價於 w.Circle.Point.X = 8w.Y = 8 // 等價於 w.Circle.Point.Y = 8w.Radius = 5 // 等價於 w.Circle.Radius = 5w.Spokes = 20

同樣的規則,內嵌類型的方法也會提升為外部類型的方法。

2. 匿名組合不是繼承

2.1 方法的接受者沒變

當我們嵌入一個類型,這個類型的方法就變成了外部類型的方法,但是當它被調用時,方法的接受者是內部類型(嵌入類型),而非外部類型。— Effective Go

type Job struct {    Command string    *log.Logger}func (job *Job)Start() {    job.Log("starting now...")    ... // 做一些事情    job.Log("started.")}

上面這個Job例子,即使組合後調用的方式變成了job.Log(...),但Log函數的接收者仍然是 log.Logger 指標,因此在Log中也不可能訪問到job的其他成員方法和變數。

2.2 內嵌類型不是基類

如果讀者對基於 來實現的物件導向語言比較熟悉的話, 可能會傾向於將 內嵌類型 看作一個基類, 而 外部類型 看作其子類或者繼承類, 或者將 外部類型 看作 "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。

2.3 匿名衝突(duplicate field) 和隱式名字

匿名成員也有一個隱式的名字,以其類型名稱(去掉包名部分)作為成員變數的名字。 因此不能同一級同時包含兩個類型相同的匿名成員, 這會導致名字衝突。

type Logger struct {    Level int}type MyJob struct {    *Logger    Name string    *log.Logger // duplicate field Logger}

以下兩點都間接說明匿名組合不是繼承:

  • 匿名成員有隱式的名字
  • 匿名可能衝突(duplicate field)

參考文章:

  • Effective Go #Embedding: https://golang.org/doc/effective_go.html#embedding
  • Golang繼承指標與非指標的一個疑問: https://segmentfault.com/q/1010000002687684/a-1020000002688273
  • [golang note] 匿名組合: https://www.cnblogs.com/heartchord/p/5254564.html
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.