# 2.6 組合
go支持組合,即一種結構體包含另外一個結構體。在一些語言中,這叫混入類或者特性。語言總是不能實現簡明的組合機制。在java中:
```java
public class Person {
private String name;
public String getName() {
return this.name;
}
}
public class Saiyan {
// 這表明`Saiyan`有一個`person`
private Person person;
// 可以使用`person`調用方法
public String getName() {
return this.person.getName();
}
...
}
```
這樣語法太繁瑣了。每個`Person`的方法在`Saiyan`中都被復寫一遍。go避免這樣繁瑣的方式:
```go
type Person struct {
Name string
}
func (p *Person) Introduce() {
fmt.Printf("Hi, I'm %s\n", p.Name)
}
type Saiyan struct {
*Person
Power int
}
// 使用:
goku := &Saiyan{
Person: &Person{"Goku"},
Power: 9001,
}
goku.Introduce()
```
結構體`Saiyan`有一個字段時`*Persion`類型。因此我們沒有明確的給它一個字段名,我們可以間接的使用這個組合類型的字段和方法。然而,go編譯器給會給該字段一個名字,認為這是完全有效的。
```go
goku := &Saiyan{
Person: &Person{"Goku"},
}
fmt.Println(goku.Name)
fmt.Println(goku.Person.Name)
```
上面代碼都將打印`Goku`。
組合優于繼承嗎?很多人都認為組合是一種更健壯的共享代碼的方式。當你使用繼承,你的類將和你的超類緊耦合,并且你最終更關注繼承,而不是行為。
## 2.6.1 重載
雖然重載不是針對結構體,但是也值得提及。簡單來說,go不支持重載。因此你會看見(和寫)很多函數諸如`Load`、`LoadById`和 `LoadByName`等等。
然而,因為匿名組合只是一個編譯技巧,我們能“重寫”一個組合類型的方法。例如,我們的結構體`Saiyan`可以定義自己的`Introduce`方法:
```go
func (s *Saiyan) Introduce() {
fmt.Printf("Hi, I'm %s. Ya!\n", s.Name)
}
```
這種組合版本總是可以通過`s.Person.Introduce()`調用`Introduce()`方法。
## 鏈接
- 關于本書
- 引言
- 準備工作
- 安裝開發環境
- 開始使用Go
- 創建一個Go模塊
- 第1章:基礎知識
- 1.1 編譯
- 1.2 靜態類型
- 1.3 類c語法
- 1.4 垃圾回收
- 1.5 運行go代碼
- 1.6 導入包
- 1.7 變量和聲明
- 1.8 函數聲明
- 1.9 繼續之前
- 第2章:語法學習
- 2.1 聲明和初始化
- 2.2 結構體上的函數
- 2.3 構造函數
- 2.4 new
- 2.5 結構體字段
- 2.6 組合
- 2.7 指針類型和值類型
- 2.8 繼續之前
- 第3章:復雜類型
- 3.1 數組
- 3.2 切片
- 3.3 映射
- 3.4 指針類型和值類型
- 3.5 繼續之前
- 第4章:面向對象
- 4.1 包
- 4.2 接口
- 4.3 繼續之前
- 第5章:綜合知識
- 5.1 錯誤處理
- 5.2 defer
- 5.3 go語言風格
- 5.4 初始化的if
- 5.5 空接口和轉換
- 5.6 字符串和字節數組
- 5.7 函數類型
- 5.8 內存分配
- 第6章:高并發
- 6.1 go協程
- 6.2 同步
- 6.3 通道
- 6.4 繼續之前
- 第7章:工具庫
- 7.1 類型轉換
- 7.2 時間操作
- 第8章:程序測試
- 單元測試
- 性能測試
- 第9章:簡單實例
- 內存分配
- 第10章:項目實戰
- 結論
- 附錄