Go 中的接口是如何表示的?

How are interfaces represented in Go?

我正在阅读两篇文章,有点困惑。

这篇文章 - http://blog.golang.org/laws-of-reflection

> var r io.Reader
tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
if err != nil {
    return nil, err
}
r = tty

r contains, schematically, the (value, type) pair, (tty, *os.File). Notice that the type *os.File implements methods other than Read; even though the interface value provides access only to the Read method, the value inside carries all the type information about that value.

另一篇文章说

In terms of our example, the itable for Stringer holding type Binary lists the methods used to satisfy Stringer, which is just String: Binary's other methods (Get) make no appearance in the itable.

感觉这两个是对立的。根据第二篇文章,第一次提取的变量r应该是(tty, io.Reader),因为那是r的静态类型。相反,文章说 *os.File 是 tty 的类型。如果第二个示例是正确的,那么第一个示例中的图表应该包含二进制类型实现的所有方法。

我哪里错了?

都对,r "holds" (tty, *os.File) 第二篇就是这么说的。请注意,Laws of Reflection 更高级一些,并没有像第二篇文章中讨论的那样提及实现细节(每个版本中可能会发生变化)。第二篇的图是这样写的:"s contains schematically (b, *Binary)。s是Stringer类型,它的数据是一个Binary,值为200,s的itable包含一个方法String和Binary的其他方法(或者*Binary) 未在 itable 中表示,因此无法访问 s.

请注意,我认为 Go 1.4 中接口的实际实现与第二篇文章(Russ 的?)所述不同。 “

这两篇文章在两个截然不同的粒度级别上解释了一个相似的概念。正如 Volker 所说,"Laws of Reflection" 是对通过反射 检查对象 时实际发生情况的基本概述。您的第二篇文章研究了接口的动态调度属性(也可以通过反射解析)以及运行时如何解析它们 at runtime

According to the second article, the variable r in the first extract should be (tty, io.Reader)

鉴于这种理解,将运行时的接口视为 "wrapper object"。它的存在是为了提供关于另一个对象的信息(你第二篇文章中的itable)以知道在包装对象布局中跳转到哪里(不同版本的实现可能不同..但大多数语言的原理基本相同) .

这就是在 r 上调用 Read 的原因.. 首先它会检查 itable 并跳转到为 os.File 类型布局的函数.如果那是一个接口......你会看到另一个取消引用和分派(IIRC 在 Go 中根本不适用)。

RE: Reflection - 你正在以 (value, type) 对的形式(通过 reflect.ValueOfreflect.TypeOf 方法)得到一个易于理解的表示形式。