## 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. 相关资源