空接口不提供任何信息(The empty interface says nothing)。- Rob Pike,Go 语言之父
Go 对接口的支持介于这两种语言的中间。在 Go 中你不必像 Java 那样显式声明某个类型实现了某个接口,这在某种意义上它与 Ruby 类似;但是另一方面,你必须声明该接口,这又与接口在 Java 等静态类型语言中的工作方式更加一致。这种不需要类型显式声明实现了某个接口的方式可以使得种类繁多的类型与接口匹配,包括那些存量的、并非由你编写的代码以及你无法编辑的代码(比如:标准库)。
Go 的这种方式兼顾安全性和灵活性,其中安全性是由 Go 编译器来保证的,而为编译器提供输入信息的恰是接口类型的定义**。比如下面的接口:
// $GOROOT/src/io/io.gotype Reader interface {Read(p []byte) (n int, err error)}
Go 编译器通过解析该接口定义得到接口的名字信息以及其方法信息,在为此接口类型参数赋值时,编译器就会根据这些信息对实参进行检查。这时,如果函数或方法的参数类型为空接口interface{},会发生什么呢?这恰好就应了本节开篇引用的 Rob Pike 的那句话:“空接口不提供任何信息”。这里“提供”一词的对象不是开发者,而是编译器。在函数或方法参数中使用空接口类型,意味着你没有为编译器提供关于传入实参数据的任何信息,因此,你将失去静态类型语言类型安全检查的 ”保护屏障“,你需要自己检查类似的错误,并且直到运行时才能发现此类错误。
在这方面,Go 标准库为我们做出了”表率“。全面搜索标准库后,你可以发现以interface{}为参数类型的方法和函数少之甚少。使用interface{}作为参数类型的函数或方法主要有两类:
- 容器算法类 - 比如:container 下的 heap、list 和 ring 包、sort 包、sync.Map 等
- 格式化/日志类 - 比如:fmt 包、log 包等
这些使用interface{}作为参数类型的函数或方法的共同特点就是它们面对的都是未知类型的数据,因此使用interface{}也可以理解为在 Go 语言尚未支持泛型这个阶段的一个权宜之计。
