a亚洲精品_精品国产91乱码一区二区三区_亚洲精品在线免费观看视频_欧美日韩亚洲国产综合_久久久久久久久久久成人_在线区

首頁(yè) > 編程 > Golang > 正文

Golang中匿名組合實(shí)現(xiàn)偽繼承的方法

2020-04-01 18:53:47
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

"Go語(yǔ)言的面向?qū)ο髾C(jī)制與一般語(yǔ)言不同。 它沒(méi)有類層次結(jié)構(gòu), 甚至可以說(shuō)沒(méi)有類; 僅僅通過(guò)組合( 而不是繼承) 簡(jiǎn)單的對(duì)象來(lái)構(gòu)建復(fù)雜的對(duì)象。" -- 《Go語(yǔ)言圣經(jīng)》

1.匿名組合

1.1 匿名組合定義 

golang中組合語(yǔ)法,就是在一個(gè)類中,引入了另一個(gè)類,如

  type Logger struct{  }  type Work struct{    log Logger  }  type Work2 struct{    log *Logger  }  func (Logger)Info(v ...interface{}){  }

如上邊代碼所示,Work類中定義了一個(gè)Logger類型的變量,這種是比較常見(jiàn)的引入方式,姑且在此稱之為非匿名組合,那什么是匿名組合呢,如其名,就是在組合的過(guò)程中,不給名字唄,如代碼所示:

type Logger struct {}type Work struct {  Logger}type Work2 struct {  *Logger}func (Logger) Info(v ...interface{}) {}

上邊的代碼中,Work類與Work2類均與Logger類匿名組合。兩個(gè)類唯一不同的是,Work2中組合的是指針類型的Logger類。

1.2 組合對(duì)象初始化 

非匿名組合初始化方式

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}

上邊是匿名組合常見(jiàn)的初始化方式。匿名組合后,被包含類得方法和屬性可以直接被使用,即使是私有變量。 

注意事項(xiàng): 

1.匿名組合多個(gè)類時(shí),不同的類存在相同的方法,會(huì)不會(huì)沖突?答案是,不同的類中,不同的方法時(shí)不會(huì)沖突的,但是在調(diào)用這個(gè)方法時(shí),需要明確是那個(gè)類中的方法,如果匿名組合進(jìn)來(lái)的類得方法,與這個(gè)類主體中的方法發(fā)生沖突,那么默認(rèn)情況下,會(huì)使用主體類中的方法。 

2.匿名組合多個(gè)類時(shí),類名相同,會(huì)不會(huì)沖突?答案是,會(huì)。就算包名不同,類名相同,也會(huì)沖突。 

示例代碼:

package mainimport(  "bufio")type Reader struct {}type Work4 struct {  Reader  bufio.Reader}

上邊代碼編譯時(shí),會(huì)提示Reader重復(fù)定義 duplicate field Reader 

原因在于,匿名組合中,沒(méi)有給引入的類命名,所以默認(rèn)采用了類名作為屬性名。如上邊wwk2這個(gè)對(duì)象在調(diào)用Logger的Info方法時(shí),可以采用wwk2.Info(“hello”),也可以采用wwk2.Logger.Info(“hello”). 

下邊附上一段完整的演示代碼,注意會(huì)報(bào)錯(cuò)哦,這段代碼包含了上邊的duplicate field Reader錯(cuò)誤:

package mainimport (  "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語(yǔ)言提供別樣的 結(jié)構(gòu)體嵌入 機(jī)制,讓一個(gè)結(jié)構(gòu)體包含另一個(gè)結(jié)構(gòu)體類型的 匿名成員 , 這樣就可以通過(guò)簡(jiǎn)單的點(diǎn)運(yùn)算符x.f來(lái)訪問(wèn)匿名成員鏈中嵌套的x.d.e.f成員。

Go語(yǔ)言有一個(gè)特性讓我們只聲明一個(gè)成員對(duì)應(yīng)的數(shù)據(jù)類型而不指名成員的名字; 這類成員就叫匿名成員。 匿名成員的數(shù)據(jù)類型必須是命名的(而不是匿名的)類型或指向一個(gè)命名的類型的指針。

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

由于有了匿名嵌入的特性, 我們可以直接訪問(wèn)內(nèi)嵌類型的成員變量而不需要給出完整的路徑:

var w Wheelw.X = 8 // 等價(jià)于 w.Circle.Point.X = 8w.Y = 8 // 等價(jià)于 w.Circle.Point.Y = 8w.Radius = 5 // 等價(jià)于 w.Circle.Radius = 5w.Spokes = 20

同樣的規(guī)則,內(nèi)嵌類型的方法也會(huì)提升為外部類型的方法。

3.1 匿名沖突(duplicate field)

匿名成員也有一個(gè)隱式的名字,以其類型名稱(去掉包名部分)作為成員變量的名字。 因此不能同一級(jí)同時(shí)包含兩個(gè)類型相同的匿名成員, 這會(huì)導(dǎo)致名字沖突。

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

4. 匿名組合不是繼承

4.1 方法的接受者沒(méi)變

當(dāng)我們嵌入一個(gè)類型,這個(gè)類型的方法就變成了外部類型的方法,但是當(dāng)它被調(diào)用時(shí),方法的接受者是內(nèi)部類型(嵌入類型),而非外部類型。— Effective Go

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

上面這個(gè)Job例子,即使組合后調(diào)用的方式變成了job.Log(...),但Log函數(shù)的接收者仍然是 log.Logger指針,因此在Log中也不可能訪問(wèn)到j(luò)ob的其他成員方法和變量。

4.1 內(nèi)嵌類型不是基類

如果讀者對(duì)基于 類 來(lái)實(shí)現(xiàn)的面向?qū)ο笳Z(yǔ)言比較熟悉的話, 可能會(huì)傾向于將 內(nèi)嵌類型 看作一個(gè)基類, 而 外部類型 看作其子類或者繼承類, 或者將 外部類型 看作 "is a" 內(nèi)嵌類型 。 但這樣理解是錯(cuò)誤的。

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)}

請(qǐng)注意上面例子中對(duì)Distance方法的調(diào)用。 Distance有一個(gè)參數(shù)是Point類型, 但q并不是一個(gè)Point類, 所以盡管q有著Point這個(gè)內(nèi)嵌類型, 我們也必須要顯式地選擇它。 嘗試直接傳q的話你會(huì)看到錯(cuò)誤:

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

一個(gè)ColoredPoint并不是一個(gè)Point, 但ColoredPoint "has a" Point, 并且它有從Point類里引入的 Distance方法。

實(shí)際上,從實(shí)現(xiàn)的角度來(lái)考慮問(wèn)題, 內(nèi)嵌字段會(huì)指導(dǎo)編譯器去生成額外的包裝方法來(lái)委托已經(jīng)聲明好的方法, 和下面的形式是等價(jià)的:

func (p ColoredPoint) Distance(q Point) float64 { return p.Point.Distance(q)}

當(dāng)Point.Distance被以上編譯器生成的包裝方法調(diào)用時(shí), 它的接收器值是p.Point, 而不是p。

4.3 匿名沖突(duplicate field) 和隱式名字

匿名成員也有一個(gè)隱式的名字,以其類型名稱(去掉包名部分)作為成員變量的名字。 因此不能同一級(jí)同時(shí)包含兩個(gè)類型相同的匿名成員, 這會(huì)導(dǎo)致名字沖突。

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

以下兩點(diǎn)都間接說(shuō)明匿名組合不是繼承:

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

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持VEVB武林網(wǎng)。


發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 人人看黄色 | 中文字幕视频免费观看 | 日精品| 欧洲美女7788成人免费视频 | 夸克满天星在线观看 | 日韩成人在线观看 | 午夜男人的天堂 | 91九色在线观看 | 亚洲一区不卡 | 精品探花| 日日干夜夜干 | 成人av网站免费 | 精品一区二区三区免费视频 | 一区二区三区四区在线 | 精品一区二区三区蜜桃 | 在线激情网 | 丰满少妇理论片 | 麻豆久久久 | 欧美一区二区二区 | 欧美日韩在线视频一区二区 | 日韩中文字幕免费在线播放 | 伊人网在线视频 | 色婷婷综合久久久久中文一区二区 | 久久久久久久久国产精品 | 先锋资源中文字幕 | 成人在线视频网址 | 亚洲欧美成人网 | 国产传媒在线 | 色站综合 | 国产精品成人在线观看 | 亚洲天天 | 免费久久网站 | 性人久久久 | 欧美日韩国产高清 | 台湾av在线| 欧美精品一区二区三区蜜桃视频 | 国产男人的天堂 | 羞羞的网站在线观看 | 免费高清av | 国产精品99视频 | 中文字幕亚洲字幕一区二区 |