mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2025-10-27 08:33:02 +00:00
回到简体
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
## 6.3. 通過嵌入結構體來擴展類型
|
||||
## 6.3. 通过嵌入结构体来扩展类型
|
||||
|
||||
來看看ColoredPoint這個類型:
|
||||
来看看ColoredPoint这个类型:
|
||||
|
||||
<u><i>gopl.io/ch6/coloredpoint</i></u>
|
||||
```go
|
||||
@@ -14,7 +14,7 @@ type ColoredPoint struct {
|
||||
}
|
||||
```
|
||||
|
||||
我們完全可以將ColoredPoint定義爲一個有三個字段的struct,但是我們卻將Point這個類型嵌入到ColoredPoint來提供X和Y這兩個字段。像我們在4.4節中看到的那樣,內嵌可以使我們在定義ColoredPoint時得到一種句法上的簡寫形式,併使其包含Point類型所具有的一切字段,然後再定義一些自己的。如果我們想要的話,我們可以直接認爲通過嵌入的字段就是ColoredPoint自身的字段,而完全不需要在調用時指出Point,比如下面這樣。
|
||||
我们完全可以将ColoredPoint定义为一个有三个字段的struct,但是我们却将Point这个类型嵌入到ColoredPoint来提供X和Y这两个字段。像我们在4.4节中看到的那样,内嵌可以使我们在定义ColoredPoint时得到一种句法上的简写形式,并使其包含Point类型所具有的一切字段,然后再定义一些自己的。如果我们想要的话,我们可以直接认为通过嵌入的字段就是ColoredPoint自身的字段,而完全不需要在调用时指出Point,比如下面这样。
|
||||
|
||||
```go
|
||||
var cp ColoredPoint
|
||||
@@ -24,7 +24,7 @@ cp.Point.Y = 2
|
||||
fmt.Println(cp.Y) // "2"
|
||||
```
|
||||
|
||||
對於Point中的方法我們也有類似的用法,我們可以把ColoredPoint類型當作接收器來調用Point里的方法,卽使ColoredPoint里沒有聲明這些方法:
|
||||
对于Point中的方法我们也有类似的用法,我们可以把ColoredPoint类型当作接收器来调用Point里的方法,即使ColoredPoint里没有声明这些方法:
|
||||
|
||||
```go
|
||||
red := color.RGBA{255, 0, 0, 255}
|
||||
@@ -37,15 +37,15 @@ q.ScaleBy(2)
|
||||
fmt.Println(p.Distance(q.Point)) // "10"
|
||||
```
|
||||
|
||||
Point類的方法也被引入了ColoredPoint。用這種方式,內嵌可以使我們定義字段特别多的複雜類型,我們可以將字段先按小類型分組,然後定義小類型的方法,之後再把它們組合起來。
|
||||
Point类的方法也被引入了ColoredPoint。用这种方式,内嵌可以使我们定义字段特别多的复杂类型,我们可以将字段先按小类型分组,然后定义小类型的方法,之后再把它们组合起来。
|
||||
|
||||
讀者如果對基於類來實現面向對象的語言比較熟悉的話,可能會傾向於將Point看作一個基類,而ColoredPoint看作其子類或者繼承類,或者將ColoredPoint看作"is a" Point類型。但這是錯誤的理解。請註意上面例子中對Distance方法的調用。Distance有一個參數是Point類型,但q併不是一個Point類,所以盡管q有着Point這個內嵌類型,我們也必須要顯式地選擇它。嚐試直接傳q的話你會看到下面這樣的錯誤:
|
||||
读者如果对基于类来实现面向对象的语言比较熟悉的话,可能会倾向于将Point看作一个基类,而ColoredPoint看作其子类或者继承类,或者将ColoredPoint看作"is a" Point类型。但这是错误的理解。请注意上面例子中对Distance方法的调用。Distance有一个参数是Point类型,但q并不是一个Point类,所以尽管q有着Point这个内嵌类型,我们也必须要显式地选择它。尝试直接传q的话你会看到下面这样的错误:
|
||||
|
||||
```go
|
||||
p.Distance(q) // compile error: cannot use q (ColoredPoint) as Point
|
||||
```
|
||||
|
||||
一個ColoredPoint併不是一個Point,但他"has a"Point,併且它有從Point類里引入的Distance和ScaleBy方法。如果你喜歡從實現的角度來考慮問題,內嵌字段會指導編譯器去生成額外的包裝方法來委託已經聲明好的方法,和下面的形式是等價的:
|
||||
一个ColoredPoint并不是一个Point,但他"has a"Point,并且它有从Point类里引入的Distance和ScaleBy方法。如果你喜欢从实现的角度来考虑问题,内嵌字段会指导编译器去生成额外的包装方法来委托已经声明好的方法,和下面的形式是等价的:
|
||||
|
||||
```go
|
||||
func (p ColoredPoint) Distance(q Point) float64 {
|
||||
@@ -57,9 +57,9 @@ func (p *ColoredPoint) ScaleBy(factor float64) {
|
||||
}
|
||||
```
|
||||
|
||||
當Point.Distance被第一個包裝方法調用時,它的接收器值是p.Point,而不是p,當然了,在Point類的方法里,你是訪問不到ColoredPoint的任何字段的。
|
||||
当Point.Distance被第一个包装方法调用时,它的接收器值是p.Point,而不是p,当然了,在Point类的方法里,你是访问不到ColoredPoint的任何字段的。
|
||||
|
||||
在類型中內嵌的匿名字段也可能是一個命名類型的指針,這種情況下字段和方法會被間接地引入到當前的類型中(譯註:訪問需要通過該指針指向的對象去取)。添加這一層間接關繫讓我們可以共享通用的結構併動態地改變對象之間的關繫。下面這個ColoredPoint的聲明內嵌了一個*Point的指針。
|
||||
在类型中内嵌的匿名字段也可能是一个命名类型的指针,这种情况下字段和方法会被间接地引入到当前的类型中(译注:访问需要通过该指针指向的对象去取)。添加这一层间接关系让我们可以共享通用的结构并动态地改变对象之间的关系。下面这个ColoredPoint的声明内嵌了一个*Point的指针。
|
||||
|
||||
```go
|
||||
type ColoredPoint struct {
|
||||
@@ -75,7 +75,7 @@ p.ScaleBy(2)
|
||||
fmt.Println(*p.Point, *q.Point) // "{2 2} {2 2}"
|
||||
```
|
||||
|
||||
一個struct類型也可能會有多個匿名字段。我們將ColoredPoint定義爲下面這樣:
|
||||
一个struct类型也可能会有多个匿名字段。我们将ColoredPoint定义为下面这样:
|
||||
|
||||
```go
|
||||
type ColoredPoint struct {
|
||||
@@ -84,11 +84,11 @@ type ColoredPoint struct {
|
||||
}
|
||||
```
|
||||
|
||||
然後這種類型的值便會擁有Point和RGBA類型的所有方法,以及直接定義在ColoredPoint中的方法。當編譯器解析一個選擇器到方法時,比如p.ScaleBy,它會首先去找直接定義在這個類型里的ScaleBy方法,然後找被ColoredPoint的內嵌字段們引入的方法,然後去找Point和RGBA的內嵌字段引入的方法,然後一直遞歸向下找。如果選擇器有二義性的話編譯器會報錯,比如你在同一級里有兩個同名的方法。
|
||||
然后这种类型的值便会拥有Point和RGBA类型的所有方法,以及直接定义在ColoredPoint中的方法。当编译器解析一个选择器到方法时,比如p.ScaleBy,它会首先去找直接定义在这个类型里的ScaleBy方法,然后找被ColoredPoint的内嵌字段们引入的方法,然后去找Point和RGBA的内嵌字段引入的方法,然后一直递归向下找。如果选择器有二义性的话编译器会报错,比如你在同一级里有两个同名的方法。
|
||||
|
||||
方法隻能在命名類型(像Point)或者指向類型的指針上定義,但是多虧了內嵌,有些時候我們給匿名struct類型來定義方法也有了手段。
|
||||
方法只能在命名类型(像Point)或者指向类型的指针上定义,但是多亏了内嵌,有些时候我们给匿名struct类型来定义方法也有了手段。
|
||||
|
||||
下面是一個小trick。這個例子展示了簡單的cache,其使用兩個包級别的變量來實現,一個mutex互斥量(§9.2)和它所操作的cache:
|
||||
下面是一个小trick。这个例子展示了简单的cache,其使用两个包级别的变量来实现,一个mutex互斥量(§9.2)和它所操作的cache:
|
||||
|
||||
```go
|
||||
var (
|
||||
@@ -104,7 +104,7 @@ func Lookup(key string) string {
|
||||
}
|
||||
```
|
||||
|
||||
下面這個版本在功能上是一致的,但將兩個包級吧的變量放在了cache這個struct一組內:
|
||||
下面这个版本在功能上是一致的,但将两个包级吧的变量放在了cache这个struct一组内:
|
||||
|
||||
```go
|
||||
var cache = struct {
|
||||
@@ -123,4 +123,4 @@ func Lookup(key string) string {
|
||||
}
|
||||
```
|
||||
|
||||
我們給新的變量起了一個更具表達性的名字:cache。因爲sync.Mutex字段也被嵌入到了這個struct里,其Lock和Unlock方法也就都被引入到了這個匿名結構中了,這讓我們能夠以一個簡單明了的語法來對其進行加鎖解鎖操作。
|
||||
我们给新的变量起了一个更具表达性的名字:cache。因为sync.Mutex字段也被嵌入到了这个struct里,其Lock和Unlock方法也就都被引入到了这个匿名结构中了,这让我们能够以一个简单明了的语法来对其进行加锁解锁操作。
|
||||
|
||||
Reference in New Issue
Block a user