## 8.6\. Interfaces(接口)
C++提供了class,類繼承和模板,類似的go語言提供了接口支持。go中的接口和C++中的純虛 基類 (只有虛函數,沒有數據成員)類似。在Go語言中,任何實現了接口的函數的類型,都可以 看作是接口的一個實現。 類型在實現某個接口的時候,不需要顯式關聯該接口的信息。接口的實現 和接口的定義完全分離了。
類型的方法和普通函數定義類似,只是前面多了一個對象接收者receiver。 對象接受者和C++中的this指針類似。
```
type myType struct { i int }
func (p *myType) get() int { return p.i }
```
方法get依附于myType類型。myType對象在 函數中對應p。
方法在命名的類型上定義。如果,改變類型的話,那么就是針對新類型的另一個函數了。
如果要在內建類型上定義方法,則需要給內建類型重新指定一個名字,然后在新指定名字的類型上 定義方法。新定義的類型和內建的類型是有區別的。
```
type myInteger int
func (p myInteger) get() int { return int(p) } // Conversion required.
func f(i int) { }
var v myInteger
// f(v) is invalid.
// f(int(v)) is valid; int(v) has no defined methods.
```
把方法抽象到接口:
```
type myInterface interface {
get() int
set(i int)
}
```
為了讓我們前面定義的myType滿足接口,需要再增加一個方法:
```
func (p *myType) set(i int) { p.i = i }
```
現在,任何以myInterface類型作為參數的函數,都可以用 *myType 類型傳遞了。
```
func getAndSet(x myInterface) {}
func f1() {
var p myType
getAndSet(&p)
}
```
以C++的觀點來看,如果把myInterface看作一個純虛基類,那么實現了 set 和 get方法的 *myType 自動成為 了從myInterface純虛基類繼承的子類了。在Go中,一個類型可以同時 實現多種接口。
使用匿名成員,我們可以模擬C++中類的繼承機制。
```
type myChildType struct { myType; j int }
func (p *myChildType) get() int { p.j++; return p.myType.get() }
```
這里的myChildType可以看作是myType的子類。
```
func f2() {
var p myChildType
getAndSet(&p)
}
```
myChildType類型中是有set方法的。在go中,匿名成員的方法 會默認被提升為類型本身的方法。 因為myChildType含有一個myType 類型的匿名成員,因此也就繼承了myType中的set方法, 另一個 get方法則相當于被重載了。
不過這和C++也不是完全等價的。當一個匿名方法被調用的時候,方法對應的類型對象 是匿名成員類型, 并不是當前類型!換言之,匿名成員上的方法并不是C++中的虛函數。 如果你需要模擬虛函數機制, 那么可以使用接口。
一個接口類型的變量可以通過接口的一個內建的特殊方法轉換為另一個接口類型變量。 這是由運行時庫動態完成的, 和C++中的dynamic_cast有些類似。 但是在Go語言中,兩個相互轉換的接口類型之間并不需要什么信息。
```
type myPrintInterface interface {
print()
}
func f3(x myInterface) {
x.(myPrintInterface).print() // type assertion to myPrintInterface
}
```
向myPrintInterface類型的轉換是動態的。它可以工作在底層實現了 print方法的變量上。
因為,這里動態類型轉換機制,我們可以用它來模擬實現C++中的模板功能。這里我們需要 定一個最小的接口:
```
type Any interface { }
```
該接口可以持有任意類型的數據,但是在使用的時候需要將該接口變量轉換為需要的類型。 因為,這里類型轉換是動態實現的,因此,沒有辦法定義像C++中的內聯函數。類型的驗證 由運行時庫完成, 我們可以調用該變量類型支持的所有方法。
```
type iterator interface {
get() Any
set(v Any)
increment()
equal(arg *iterator) bool
}
```
- 1. 關于本文
- 2. Go語言簡介
- 3. 安裝go環境
- 3.1. 簡介
- 3.2. 安裝C語言工具
- 3.3. 安裝Mercurial
- 3.4. 獲取代碼
- 3.5. 安裝Go
- 3.6. 編寫程序
- 3.7. 進一步學習
- 3.8. 更新go到新版本
- 3.9. 社區資源
- 3.10. 環境變量
- 4. Go語言入門
- 4.1. 簡介
- 4.2. Hello,世界
- 4.3. 分號(Semicolons)
- 4.4. 編譯
- 4.5. Echo
- 4.6. 類型簡介
- 4.7. 申請內存
- 4.8. 常量
- 4.9. I/O包
- 4.10. Rotting cats
- 4.11. Sorting
- 4.12. 打印輸出
- 4.13. 生成素數
- 4.14. Multiplexing
- 5. Effective Go
- 5.1. 簡介
- 5.2. 格式化
- 5.3. 注釋
- 5.4. 命名
- 5.5. 分號
- 5.6. 控制流
- 5.7. 函數
- 5.8. 數據
- 5.9. 初始化
- 5.10. 方法
- 5.11. 接口和其他類型
- 5.12. 內置
- 5.13. 并發
- 5.14. 錯誤處理
- 5.15. Web服務器
- 6. 如何編寫Go程序
- 6.1. 簡介
- 6.2. 社區資源
- 6.3. 新建一個包
- 6.4. 測試
- 6.5. 一個帶測試的演示包
- 7. Codelab: 編寫Web程序
- 7.1. 簡介
- 7.2. 開始
- 7.3. 數據結構
- 7.4. 使用http包
- 7.5. 基于http提供wiki頁面
- 7.6. 編輯頁面
- 7.7. template包
- 7.8. 處理不存在的頁面
- 7.9. 儲存頁面
- 7.10. 錯誤處理
- 7.11. 模板緩存
- 7.12. 驗證
- 7.13. 函數文本和閉包
- 7.14. 試試!
- 7.15. 其他任務
- 8. 針對C++程序員指南
- 8.1. 概念差異
- 8.2. 語法
- 8.3. 常量
- 8.4. Slices(切片)
- 8.5. 構造值對象
- 8.6. Interfaces(接口)
- 8.7. Goroutines
- 8.8. Channels(管道)
- 9. 內存模型
- 9.1. 簡介
- 9.2. Happens Before
- 9.3. 同步(Synchronization)
- 9.4. 錯誤的同步方式
- 10. 附錄
- 10.1. 命令行工具
- 10.2. 視頻和講座
- 10.3. Release History
- 10.4. Go Roadmap
- 10.5. 相關資源