今天就跟大家聊聊有關(guān)怎么在Golang中通過匿名組合實現(xiàn)偽繼承,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。
公司主營業(yè)務(wù):成都做網(wǎng)站、網(wǎng)站建設(shè)、移動網(wǎng)站開發(fā)等業(yè)務(wù)。幫助企業(yè)客戶真正實現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競爭能力。創(chuàng)新互聯(lián)是一支青春激揚、勤奮敬業(yè)、活力青春激揚、勤奮敬業(yè)、活力澎湃、和諧高效的團(tuán)隊。公司秉承以“開放、自由、嚴(yán)謹(jǐn)、自律”為核心的企業(yè)文化,感謝他們對我們的高要求,感謝他們從不同領(lǐng)域給我們帶來的挑戰(zhàn),讓我們激情的團(tuán)隊有機(jī)會用頭腦與智慧不斷的給客戶帶來驚喜。創(chuàng)新互聯(lián)推出湖口免費做網(wǎng)站回饋大家。
1.匿名組合
1.1 匿名組合定義
golang中組合語法,就是在一個類中,引入了另一個類,如
type Logger struct{ } type Work struct{ log Logger } type Work2 struct{ log *Logger } func (Logger)Info(v ...interface{}){ }
如上邊代碼所示,Work類中定義了一個Logger類型的變量,這種是比較常見的引入方式,姑且在此稱之為非匿名組合,那什么是匿名組合呢,如其名,就是在組合的過程中,不給名字唄,如代碼所示:
type Logger struct { } type Work struct { Logger } type Work2 struct { *Logger } func (Logger) Info(v ...interface{}) { }
上邊的代碼中,Work類與Work2類均與Logger類匿名組合。兩個類唯一不同的是,Work2中組合的是指針類型的Logger類。
1.2 組合對象初始化
非匿名組合初始化方式
func main(){ var wk = Work{log:Logger{}} var wwk = Work{Logger{}} //...and so on var wk2 = Work2{log:new(Logger)} var wwk2 = Work2{new(Logger)} //... and so on }
匿名組合初始化
func main(){ var wk = Work{Logger{}} var wwk = Work{Logger:Logger{}} //... and so on var wk2 = Work2{new(Logger)} var wwk2 = Work2{Logger:&Logger{}} //... and so on }
上邊是匿名組合常見的初始化方式。匿名組合后,被包含類得方法和屬性可以直接被使用,即使是私有變量。
注意事項:
1.匿名組合多個類時,不同的類存在相同的方法,會不會沖突?答案是,不同的類中,不同的方法時不會沖突的,但是在調(diào)用這個方法時,需要明確是那個類中的方法,如果匿名組合進(jìn)來的類得方法,與這個類主體中的方法發(fā)生沖突,那么默認(rèn)情況下,會使用主體類中的方法。
2.匿名組合多個類時,類名相同,會不會沖突?答案是,會。就算包名不同,類名相同,也會沖突。
示例代碼:
package main import( "bufio" ) type Reader struct { } type Work4 struct { Reader bufio.Reader }
上邊代碼編譯時,會提示Reader重復(fù)定義 duplicate field Reader
原因在于,匿名組合中,沒有給引入的類命名,所以默認(rèn)采用了類名作為屬性名。如上邊wwk2這個對象在調(diào)用Logger的Info方法時,可以采用wwk2.Info(“hello”),也可以采用wwk2.Logger.Info(“hello”).
下邊附上一段完整的演示代碼,注意會報錯哦,這段代碼包含了上邊的duplicate field Reader錯誤:
package main import ( "bufio" "fmt" ) type Logger struct { } type Work struct { Logger } type Work2 struct { *Logger } type Work3 struct { log *Logger } type Reader struct { } type Work4 struct { Reader bufio.Reader } func (Logger) Info(v ...interface{}) { fmt.Println(v...) } func main() { var wk = Work{Logger{}} wk.Info("hello: Work{Logger{}}") var wwk = Work{Logger: Logger{}} wwk.Info("hello: Work{Logger: Logger{}}") //... and so on var wk2 = Work2{new(Logger)} wk2.Info("hello: Work2{new(Logger)}") var wwk2 = Work2{Logger: &Logger{}} wwk2.Info("hello: Work2{Logger: &Logger{}}") wwk2.Logger.Info("hello: wwk2.Logger.Info") var wk3 = Work3{new(Logger)} wk3.log.Info("hello: Work3{new(Logger)}") }
3. 結(jié)構(gòu)體嵌入和匿名成員
Go語言提供別樣的 結(jié)構(gòu)體嵌入 機(jī)制,讓一個結(jié)構(gòu)體包含另一個結(jié)構(gòu)體類型的 匿名成員 , 這樣就可以通過簡單的點運算符x.f來訪問匿名成員鏈中嵌套的x.d.e.f成員。
Go語言有一個特性讓我們只聲明一個成員對應(yīng)的數(shù)據(jù)類型而不指名成員的名字; 這類成員就叫匿名成員。 匿名成員的數(shù)據(jù)類型必須是命名的(而不是匿名的)類型或指向一個命名的類型的指針。
type Circle struct { Point Radius int } type Wheel struct { Circle Spokes int }
由于有了匿名嵌入的特性, 我們可以直接訪問內(nèi)嵌類型的成員變量而不需要給出完整的路徑:
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
同樣的規(guī)則,內(nèi)嵌類型的方法也會提升為外部類型的方法。
3.1 匿名沖突(duplicate field)
匿名成員也有一個隱式的名字,以其類型名稱(去掉包名部分)作為成員變量的名字。 因此不能同一級同時包含兩個類型相同的匿名成員, 這會導(dǎo)致名字沖突。
type Logger struct { Level int } type MyJob struct { *Logger Name string *log.Logger // duplicate field Logger }
4. 匿名組合不是繼承
4.1 方法的接受者沒變
當(dāng)我們嵌入一個類型,這個類型的方法就變成了外部類型的方法,但是當(dāng)它被調(diào)用時,方法的接受者是內(nèi)部類型(嵌入類型),而非外部類型?!?Effective Go
type Job struct { Command string *log.Logger } func (job *Job)Start() { job.Log("starting now...") ... // 做一些事情 job.Log("started.") }
上面這個Job例子,即使組合后調(diào)用的方式變成了job.Log(...),但Log函數(shù)的接收者仍然是 log.Logger指針,因此在Log中也不可能訪問到j(luò)ob的其他成員方法和變量。
4.1 內(nèi)嵌類型不是基類
如果讀者對基于 類 來實現(xiàn)的面向?qū)ο笳Z言比較熟悉的話, 可能會傾向于將 內(nèi)嵌類型 看作一個基類, 而 外部類型 看作其子類或者繼承類, 或者將 外部類型 看作 "is a" 內(nèi)嵌類型 。 但這樣理解是錯誤的。
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方法的調(diào)用。 Distance有一個參數(shù)是Point類型, 但q并不是一個Point類, 所以盡管q有著Point這個內(nèi)嵌類型, 我們也必須要顯式地選擇它。 嘗試直接傳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方法。
實際上,從實現(xiàn)的角度來考慮問題, 內(nèi)嵌字段會指導(dǎo)編譯器去生成額外的包裝方法來委托已經(jīng)聲明好的方法, 和下面的形式是等價的:
func (p ColoredPoint) Distance(q Point) float64 { return p.Point.Distance(q) }
當(dāng)Point.Distance被以上編譯器生成的包裝方法調(diào)用時, 它的接收器值是p.Point, 而不是p。
4.3 匿名沖突(duplicate field) 和隱式名字
匿名成員也有一個隱式的名字,以其類型名稱(去掉包名部分)作為成員變量的名字。 因此不能同一級同時包含兩個類型相同的匿名成員, 這會導(dǎo)致名字沖突。
type Logger struct { Level int } type MyJob struct { *Logger Name string *log.Logger // duplicate field Logger }
以下兩點都間接說明匿名組合不是繼承:
匿名成員有隱式的名字
匿名可能沖突(duplicate field)
golang 是Google開發(fā)的一種靜態(tài)強(qiáng)類型、編譯型、并發(fā)型,并具有垃圾回收功能的編程語言,其語法與 C語言相近,但并不包括如枚舉、異常處理、繼承、泛型、斷言、虛函數(shù)等功能。
看完上述內(nèi)容,你們對怎么在Golang中通過匿名組合實現(xiàn)偽繼承有進(jìn)一步的了解嗎?如果還想了解更多知識或者相關(guān)內(nèi)容,請關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝大家的支持。