interface 变量的值和类型

变量包括(type, value)两部分。

type 包括 static type 和 concrete type。

  • static type 是你在编码是看见的类型 (如 int、string),
  • concrete type 是 runtime 系统看见的类型。

类型断言能否成功,取决于变量的 concrete type,而不是 static type。因此,一个 reader 变量如果它的 concrete type 也实现了 write 方法的话,它也可以被类型断言为 writer。

反射,就是建立在类型之上的,Golang 的指定类型的变量的类型是静态的(也就是指定 int、string 这些的变量,它的 type 是 static type),在创建变量的时候就已经确定,反射主要与 Golang 的 interface 类型相关(它的 type 是 concrete type),只有 interface 类型才有反射一说。

在 Golang 的实现中,每个 interface 变量都有一个对应 pair,pair 中记录了实际变量的值和类型:

1
(value, type)
  • value 是实际变量值,
  • type 是实际变量的类型。

一个 interface{} 类型的变量包含了 2 个指针:

  • 指针指向值的类型【对应 concrete type】,
  • 指针指向实际的值【对应 value】。

例如,创建类型为 *os.File 的变量,然后将其赋给一个接口变量 r:

1
2
3
4
tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)

var r io.Reader
r = tty

接口变量 r 的 pair 中将记录如下信息:(tty, *os.File)

这个 pair 在接口变量的连续赋值过程中是不变的,将接口变量 r 赋给另一个接口变量 w:

1
2
var w io.Writer
w = r.(io.Writer)

接口变量 w 的 pair 与 r 的 pair 相同,都是:(tty, *os.File),即使 w 是空接口类型,pair 也是不变的。

interface 及其 pair 的存在,是 Golang 中实现反射的前提,理解了 pair,就更容易理解反射。反射就是用来检测存储在接口变量内部 (值 value;类型 concrete type) pair 对的一种机制。