## 5.12\. 內置
Go 不提供通行的、類型驅動的子類劃分的概念,但它通過在結構或界面內置,確實有能力從某實現“借”些片段。
界面內置非常簡單。我們提到過 io.Reader 和 io.Writer 的界面;這里是其定義:
```
type Reader interface {
Read(p []byte) (n int, err os.Error)
}
type Writer interface {
Write(p []byte) (n int, err os.Error)
}
```
io 包也導出一些其它的界面來規范實現其多個方法的物件。例如,io.ReadWriter 界面同時包括 Read 和 Write 。我們可以明確的列出這兩個方法來規定 io.ReadWriter ,但更簡單更有啟發的是內置這兩個界面形成新界面,如下:
```
// ReadWrite is the interface that groups the basic Read and Write methods.
type ReadWriter interface {
Reader
Writer
}
```
顧名思意,ReadWriter 可做 Reader 和 Writer 兩個的事;它是內置界面的集合(必須是沒有交集的方法)。只有界面可以內置在界面里。
同樣的基本概念適用于結構,但牽連更廣。bu?o 包有兩個結構類型,bu?o.Reader 和 bu?o.Writer ,每個都當然對應實現著 io 包的界面。并且,bu?o 也實現了緩沖的讀寫,這是通過把 reader 和 writer 內置到一個結構做到的;它列出類型但不給名稱:
```
// ReadWriter stores pointers to a Reader and a Writer.
// It implements io.ReadWriter.
type ReadWriter struct {
*Reader // *bufio.Reader
*Writer // *bufio.Writer
}
```
內置元素是指針所以使用前必須初始化指向有效的結構。 ReadWriter 結構可以寫為:
```
type ReadWriter struct {
reader *Reader
writer *Writer
}
```
但要提升域的方法又要滿足 io 界面,我們還需提供轉發方法,如:
```
func (rw *ReadWriter) Read(p []byte) (n int, err os.Error) {
return rw.reader.Read(p)
}
```
通過直接內置結構,我們避免了這些賬目管理。內置類型的方法是附送的,亦即 bu?o.ReadWriter 除了有 bu?o.Reader 和bu?o.Writer 的方法,還同時滿足三個界面:io.Reader,io.Writer 和 io.ReadWriter 。
內置和子類劃分有著重要不同。我們內置類型時,此類的方法成為外層類型的方法,但調用時其接受者是內層類型,而不是外層。我們的例子里,當 bu?o.ReadWriter 的 Read 方法被調用時,它的效果和上面所寫的轉發方法完全一樣;接受者是 ReadWriter 的 reader,而不是 ReadWriter 自身。
內置也用來提供某種便利。下例是一個內置域和普通的,帶名的域在一起:
```
type Job struct {
Command string
*log.Logger
}
```
此時 Job 類型有 Log,Logf 和其它 *log.Logger 的方法。 我們當然可以給 Logger 個名字,但無此必要。這里,初始化后,我們可以 log Job:
```
job.Log("starting now...")
```
Logger 是結構的普通域所以我們能用通常的架構函數初始化它:
```
func NewJob(command string, logger *log.Logger) *Job {
return
&Job{command, logger}
}
```
或使用組合字面:
```
job := &Job{command, log.New(os.Stderr, nil, "Job: ", log.Ldate)}
```
如果我們需要直接引用內置域,域的類型名,忽略包標識,可作為域名。如果我們要得到 Job 變量 job 的 *log.Logger,我們用job.Logger 。這可用在細化 Logger 的方法上:
```
func (job *Job) Logf(format string, args ...) {
job.Logger.Logf("%q: %s", job.Command, fmt.Sprintf(format, args))
}
```
內置類型導致撞名的問題,但解決方案很簡單。首先,域或方法 X 隱藏此類型更深層部分的 X 項。如果 log.Logger 包括叫 Command 的域或方法,則 Job 的 Command 域占主導權。
其次,如果同層出現同名,通常是個錯誤;如果 Job 結構有另一域或方法叫 Logger,要內置 log.Logger 會出錯。但只要重名在類型定義外的程序中不被提及,就不成問題。此條件提供了改動外部的內置類型的某種保護;只要兩個域都沒被用到,新增的域名和另一子類的域重名也沒有問題。
- 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. 相關資源