|
@@ -221,8 +221,94 @@ func (b Bar) ImplementsFooer() {} func (b Bar) Foo() {}
|
|
|
|
|
|
|
|
## 11.12.5 空接口和函数重载
|
|
## 11.12.5 空接口和函数重载
|
|
|
|
|
|
|
|
|
|
+在 6.1 节中, 我们看到函数重载是不被允许的。在 Go 语言中函数重载可以用可变参数 `...T` 作为函数最后一个参数来实现(参见 6.3 节)。如果我们把 T 换为空接口,那么可以知道任何类型的变量都是满足 T (空接口)类型的,这样就允许我们传递任何数据任何类型的参数给函数,即重载的实际含义。
|
|
|
|
|
|
|
|
|
|
+函数 `fmt.Printf` 就是这样做的:
|
|
|
|
|
|
|
|
|
|
+```go
|
|
|
|
|
+fmt.Printf(format string, a ...interface{}) (n int, errno error)
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+这个函数通过枚举 `slice` 类型的实参动态确定所有参数的类型。并查看每个类型是否实现了 `String()` 方法,如果是就用于产生输出信息。我们可以返回 11.10 节查看这些细节。
|
|
|
|
|
+
|
|
|
|
|
+## 11.12.6 接口的继承
|
|
|
|
|
+
|
|
|
|
|
+当一个类型包含(内嵌)另一个类型(实现了一个或多个接口)的指针时,这个类型就可以使用(另一个类型)所有的接口方法。
|
|
|
|
|
+
|
|
|
|
|
+例如:
|
|
|
|
|
+
|
|
|
|
|
+```go
|
|
|
|
|
+type Task struct {
|
|
|
|
|
+ Command string
|
|
|
|
|
+ *log.Logger
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+这个类型的工厂方法像这样:
|
|
|
|
|
+
|
|
|
|
|
+```go
|
|
|
|
|
+func NewTask(command string, logger *log.Logger) *Task {
|
|
|
|
|
+ return &Task{command, logger}
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+当 `log.Logger` 实现了 `Log()` 方法后,Task 的实例 task 就可以调用该方法:
|
|
|
|
|
+
|
|
|
|
|
+```go
|
|
|
|
|
+task.Log()
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+类型可以通过继承多个接口来提供像 `多重继承` 一样的特性:
|
|
|
|
|
+
|
|
|
|
|
+```go
|
|
|
|
|
+type ReaderWriter struct {
|
|
|
|
|
+ *io.Reader
|
|
|
|
|
+ *io.Writer
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+上面概述的原理被应用于整个 Go 包,多态用得越多,代码就相对越少(参见 12.8 节)。这被认为是 Go 编程的一种重要的最佳实践。
|
|
|
|
|
+
|
|
|
|
|
+有用的接口可以在开发的过程中被归纳出来。添加新接口非常容易,因为已有的类型不用变动(仅仅需要实现新接口的方法)。已有的函数可以扩展为使用接口类型的约束性参数:通常只有函数签名需要改变。对比基于类的 OO 类型的语言在这种情况下则需要适应整个类层次结构的变化。
|
|
|
|
|
+
|
|
|
|
|
+**练习 11.11**:[map_function_interface.go](exercises/chapter_11/map_function_interface.go):
|
|
|
|
|
+
|
|
|
|
|
+在练习 7.13 中我们定义了一个 map 函数来使用 int 切片 (map_function.go)。
|
|
|
|
|
+
|
|
|
|
|
+通过空接口和类型断言,现在我们可以写一个可以应用于许多类型的 `泛型` 的 map 函数,为 int 和 string 构建一个把 int 值加倍和连接字符串值的 map 函数 `mapFunc`。
|
|
|
|
|
+
|
|
|
|
|
+提示:为了可读性可以定义一个 interface{} 的别名,比如:type obj interface{}
|
|
|
|
|
+
|
|
|
|
|
+**练习 11.12**:[map_function_interface_var.go](exercises/chapter_11/map_function_interface_var.go):
|
|
|
|
|
+
|
|
|
|
|
+稍微改变练习 11.9,允许 `mapFunc` 接收不定数量的 items。
|
|
|
|
|
+
|
|
|
|
|
+**练习 11.13**:[main_stack.go—stack/stack_general.go](exercises/chapter_11/main_stack.go—stack/stack_general.go):
|
|
|
|
|
+
|
|
|
|
|
+在练习 10.10 和 10.11 中我们开发了一些栈结构类型。但是它们被限制为某种固定的内建类型。现在用一个元素类型是 interface{}(空接口)的切片开发一个通用的栈类型。
|
|
|
|
|
+
|
|
|
|
|
+实现下面的栈方法:
|
|
|
|
|
+
|
|
|
|
|
+```go
|
|
|
|
|
+Len() int
|
|
|
|
|
+IsEmpty() bool
|
|
|
|
|
+Push(x interface{})
|
|
|
|
|
+Pop() (x interface{}, error)
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+写一个 `Pop()` 改变栈并返回最顶部的元素;并写一个 `Top()` 只返回最顶部元素。
|
|
|
|
|
+
|
|
|
|
|
+在主程序中构建一个充满不同类型元素的栈,然后弹出并打印所有元素的值。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+## 链接
|
|
|
|
|
|
|
|
|
|
+- [目录](directory.md)
|
|
|
|
|
+- 上一节:[Printf 和反射](11.11.md)
|
|
|
|
|
+- 下一节:[总结:Go 中的面向对象](11.13.md)
|