W3Cschool
恭喜您成為首批注冊(cè)用戶(hù)
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
原文鏈接:https://gopl-zh.github.io/ch6/ch6-03.html
來(lái)看看ColoredPoint這個(gè)類(lèi)型:
gopl.io/ch6/coloredpoint
import "image/color"
type Point struct{ X, Y float64 }
type ColoredPoint struct {
Point
Color color.RGBA
}
我們完全可以將ColoredPoint定義為一個(gè)有三個(gè)字段的struct,但是我們卻將Point這個(gè)類(lèi)型嵌入到ColoredPoint來(lái)提供X和Y這兩個(gè)字段。像我們?cè)?.4節(jié)中看到的那樣,內(nèi)嵌可以使我們?cè)诙xColoredPoint時(shí)得到一種句法上的簡(jiǎn)寫(xiě)形式,并使其包含Point類(lèi)型所具有的一切字段,然后再定義一些自己的。如果我們想要的話(huà),我們可以直接認(rèn)為通過(guò)嵌入的字段就是ColoredPoint自身的字段,而完全不需要在調(diào)用時(shí)指出Point,比如下面這樣。
var cp ColoredPoint
cp.X = 1
fmt.Println(cp.Point.X) // "1"
cp.Point.Y = 2
fmt.Println(cp.Y) // "2"
對(duì)于Point中的方法我們也有類(lèi)似的用法,我們可以把ColoredPoint類(lèi)型當(dāng)作接收器來(lái)調(diào)用Point里的方法,即使ColoredPoint里沒(méi)有聲明這些方法:
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.ScaleBy(2)
q.ScaleBy(2)
fmt.Println(p.Distance(q.Point)) // "10"
Point類(lèi)的方法也被引入了ColoredPoint。用這種方式,內(nèi)嵌可以使我們定義字段特別多的復(fù)雜類(lèi)型,我們可以將字段先按小類(lèi)型分組,然后定義小類(lèi)型的方法,之后再把它們組合起來(lái)。
讀者如果對(duì)基于類(lèi)來(lái)實(shí)現(xiàn)面向?qū)ο蟮恼Z(yǔ)言比較熟悉的話(huà),可能會(huì)傾向于將Point看作一個(gè)基類(lèi),而ColoredPoint看作其子類(lèi)或者繼承類(lèi),或者將ColoredPoint看作"is a" Point類(lèi)型。但這是錯(cuò)誤的理解。請(qǐng)注意上面例子中對(duì)Distance方法的調(diào)用。Distance有一個(gè)參數(shù)是Point類(lèi)型,但q并不是一個(gè)Point類(lèi),所以盡管q有著Point這個(gè)內(nèi)嵌類(lèi)型,我們也必須要顯式地選擇它。嘗試直接傳q的話(huà)你會(huì)看到下面這樣的錯(cuò)誤:
p.Distance(q) // compile error: cannot use q (ColoredPoint) as Point
一個(gè)ColoredPoint并不是一個(gè)Point,但他"has a"Point,并且它有從Point類(lèi)里引入的Distance和ScaleBy方法。如果你喜歡從實(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)
}
func (p *ColoredPoint) ScaleBy(factor float64) {
p.Point.ScaleBy(factor)
}
當(dāng)Point.Distance被第一個(gè)包裝方法調(diào)用時(shí),它的接收器值是p.Point,而不是p,當(dāng)然了,在Point類(lèi)的方法里,你是訪(fǎng)問(wèn)不到ColoredPoint的任何字段的。
在類(lèi)型中內(nèi)嵌的匿名字段也可能是一個(gè)命名類(lèi)型的指針,這種情況下字段和方法會(huì)被間接地引入到當(dāng)前的類(lèi)型中(譯注:訪(fǎng)問(wèn)需要通過(guò)該指針指向的對(duì)象去?。?。添加這一層間接關(guān)系讓我們可以共享通用的結(jié)構(gòu)并動(dòng)態(tài)地改變對(duì)象之間的關(guān)系。下面這個(gè)ColoredPoint的聲明內(nèi)嵌了一個(gè)*Point的指針。
type ColoredPoint struct {
*Point
Color color.RGBA
}
p := ColoredPoint{&Point{1, 1}, red}
q := ColoredPoint{&Point{5, 4}, blue}
fmt.Println(p.Distance(*q.Point)) // "5"
q.Point = p.Point // p and q now share the same Point
p.ScaleBy(2)
fmt.Println(*p.Point, *q.Point) // "{2 2} {2 2}"
一個(gè)struct類(lèi)型也可能會(huì)有多個(gè)匿名字段。我們將ColoredPoint定義為下面這樣:
type ColoredPoint struct {
Point
color.RGBA
}
然后這種類(lèi)型的值便會(huì)擁有Point和RGBA類(lèi)型的所有方法,以及直接定義在ColoredPoint中的方法。當(dāng)編譯器解析一個(gè)選擇器到方法時(shí),比如p.ScaleBy,它會(huì)首先去找直接定義在這個(gè)類(lèi)型里的ScaleBy方法,然后找被ColoredPoint的內(nèi)嵌字段們引入的方法,然后去找Point和RGBA的內(nèi)嵌字段引入的方法,然后一直遞歸向下找。如果選擇器有二義性的話(huà)編譯器會(huì)報(bào)錯(cuò),比如你在同一級(jí)里有兩個(gè)同名的方法。
方法只能在命名類(lèi)型(像Point)或者指向類(lèi)型的指針上定義,但是多虧了內(nèi)嵌,有些時(shí)候我們給匿名struct類(lèi)型來(lái)定義方法也有了手段。
下面是一個(gè)小trick。這個(gè)例子展示了簡(jiǎn)單的cache,其使用兩個(gè)包級(jí)別的變量來(lái)實(shí)現(xiàn),一個(gè)mutex互斥量(§9.2)和它所操作的cache:
var (
mu sync.Mutex // guards mapping
mapping = make(map[string]string)
)
func Lookup(key string) string {
mu.Lock()
v := mapping[key]
mu.Unlock()
return v
}
下面這個(gè)版本在功能上是一致的,但將兩個(gè)包級(jí)別的變量放在了cache這個(gè)struct一組內(nèi):
var cache = struct {
sync.Mutex
mapping map[string]string
}{
mapping: make(map[string]string),
}
func Lookup(key string) string {
cache.Lock()
v := cache.mapping[key]
cache.Unlock()
return v
}
我們給新的變量起了一個(gè)更具表達(dá)性的名字:cache。因?yàn)閟ync.Mutex字段也被嵌入到了這個(gè)struct里,其Lock和Unlock方法也就都被引入到了這個(gè)匿名結(jié)構(gòu)中了,這讓我們能夠以一個(gè)簡(jiǎn)單明了的語(yǔ)法來(lái)對(duì)其進(jìn)行加鎖解鎖操作。
![]() | ![]() |
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話(huà):173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: