Go 语言中的 interface{} 是什么? - All About Free

什么是interface{}

简单的来说, interface{} 是指两个内容:

  • 一堆方法 (a set of methods)
  • 同时也是一个类型

对于Go中的interface{}, 通常指一个空的接口(empty interface), 也就是说这个interface并没有任何方法.

不像Java, 可以使用implement这种关键词来手动继承接口, 所以在Go中, 所有的类型(type), 即便一个不包含任何方法的类型(type), 都会自动的继承空的接口(也就是interface{}, 上一段中说的空的接口).

所以, 如果一个函数用interface{}作为参数, 那么意味着这个参数可以接受任何类型的值.

困扰么?

假设我们有一个函数, 定义如下

func DoSomething(v interface{}) {
   // ...
}

那么在DoSomething的函数内部, v的类型是什么?

v是任何类型 (v is of any type)

但这种观点显然是错误的, v并不是任何类型, 而是interface{}类型.

在调用DoSomething, 并且传递一个参数给DoSomething的时候, Go运行时会在必要的阶段执行一次类型转换(type conversion), 并且把这个传递的值转换为一个interface{}类型的值. 所以从运行时的角度看, 任何值都是一个类型的, 而且interface{}v的一个静态类型(static type).

一个interface类型的值的结构是什么样的?

为了更进一步了解, 参考了一下Go Data Structures: Interfaces的部分内容

假设一个interface定义如下

type Stringer interface {
    String() string
}

Interface values are represented as a two-word pair giving a pointer to information about the type stored in the interface and a pointer to the associated data.
Assigning b to an interface value of type Stringer sets both words of the interface value.

interface类型的值通常包含两部分内容

  • 一个指向该类型的方法表的指针
  • 一个指向该类型的内容的指针

The first word in the interface value points at what I call an interface table or itable.

那么第一个word指向的是一个接口表itable(an interface table), 这段数据开头拥有一些关于需要的类的元信息(metadata), 之后便是一个储存各个方法的指针的列表.

Note that the itable corresponds to the interface type, not the dynamic type.

所以说itable只包含和interface{}类型有关的方法, 而不包含一个动态类型的所有方法.

换句话说, Stringer这个类型的itable虽然申明了需要Binary这个类, 但是itable的方法列表中却只有String这个方法, 而Binary类中的其他方法(比如Get)并没有出现在itable中.

The second word in the interface value points at the actual data.

interface结构中的第二个word是一个指向值的内存空间的指针, 也就是说Go运行时会开辟一段新的内存储存具体的值的内容. 所以当我们使用var s Stringer = b来申明s的时候, 其实是复制了b的内容, 而不是直接将指针指向b的内存地址. 所以当我们修改b的内容的时候, s的内容不会被改变.

通常情况下, 我们储存在interface中的值可能非常大, 但对于interface类型来说, 运行时只利用了1个word的大小来储存指针, 所以Go在堆中开辟了一大块内存, 并且用1个word大小的内存来记录这块内存的指针.

Reference

Go: What’s the meaning of interface{}?

Go Data Structures: Interfaces

Free /
Published under (CC) BY-NC-SA in categories golang