Go和Java在接口上有什么区别?

fdi*_*ing 3 java interface go

最近有人问我一个问题,Golang 和 Java 在interface 上有什么区别?

我知道存在一些“语法糖级别”差异,我感兴趣的是地下的任何东西,比如 Golang 和 Java 是如何实现接口的?最大的区别是什么?哪一种更有效率?为什么?

任何人都可以发布有关此主题的博客链接或源代码吗?源代码更好。

谢谢。

小智 6

Go 数据结构: Russ Cox 的接口

Go 的接口——静态的,在编译时检查,在需要时动态检查
Go 的接口让您可以像在纯动态语言(如 Python)中使用鸭子类型,但仍然让编译器捕获明显的错误,例如将 int 传递给带有 Read 方法的对象是预期的,或者像使用错误数量的参数调用 Read 方法。
不过,接口不限于静态检查。您可以动态检查特定接口值是否具有附加方法。


具有方法的接口值语言通常属于两个阵营之一:静态地为所有方法调用准备表(如在 C++ 和 Java 中),或在每次调用时进行方法查找(如在 Smalltalk 及其许多模仿者中,包括 JavaScript 和 Python ) 并添加花哨的缓存以提高调用效率。Go 介于两者之间:它有方法表,但在运行时计算它们。我不知道 Go 是否是第一种使用这种技术的语言,但它肯定不是常见的。
接口值表示为两个字对,给出一个指向存储在接口中的类型信息的指针和一个指向相关数据的指针。将 b 分配给 Stringer 类型的接口值会设置接口值的两个字。

接口值中的第一个词指向我所说的接口表或 itable(发音为 i-table;在运行时源中)。itable 以一些关于所涉及类型的元数据开始,然后成为一个函数指针列表。注意 itable 对应的是接口类型,而不是动态类型。
接口值中的第二个字指向实际数据,在本例中是 b 的副本。

Go 的动态类型转换意味着编译器或链接器预先计算所有可能的 itables 是不合理的:有太多(接口类型,具体类型)对,大多数都不需要。相反,编译器为每个具体类型生成一个类型描述结构,如 Binary 或 int 或 func(map[int]string)。在其他元数据中,类型描述结构包含由该类型实现的方法列表。类似地,编译器为每个接口类型生成一个(不同的)类型描述结构,比如 Stringer;它也包含一个方法列表。接口运行时通过在具体类型的方法表中查找接口类型的方法表中列出的每个方法来计算 itable。运行时在生成 itable 后缓存它,

方法查找性能
Smalltalk 和许多遵循它的动态系统在每次调用方法时都会执行方法查找。为了速度,许多实现在每个调用点使用一个简单的单入口缓存,通常在指令流本身中。在多线程程序中,必须小心管理这些缓存,因为多个线程可能同时位于同一个调用站点。即使避免了竞争,缓存最终也会成为内存争用的来源。

因为 Go 有静态类型的提示来配合动态方法查找,所以它可以将查找从调用站点移回值存储在接口中的点。


Go 接口调度是如何工作的?

接口变量上的方法调度与 vtable 调度相同。
具体类型第一次遇到接口类型时,它会构建一个指向 vtable 的哈希表条目。相同类型的第二次和后续分配将执行更便宜的哈希查找以找到 vtable。但是方法 dispatch 本身总是等价于 vtable 查找。


规格:接口类型

有关更多详细信息,请参阅:Go:interface{} 的含义是什么?


这里有两个有趣的 Go 接口用例: 为什么 Golang 需要接口?


错误类型是接口类型:如何比较Golang错误对象


计算 4 种不同形状的面积:圆形、方形、矩形和三角形:
解释 Go 中的类型断言


在 Go 中,您不需要做任何特殊的事情,例如 Java 关键字implements来实现接口,在 Go 中,您的类型只要具有正确签名的方法就足够了。

这是代码(在Go Playground上试试):

package main

import "fmt"

type Work struct {
    Name string
}

func (t Work) String() string {
    return "Stringer called."
}

func main() {
    w := Work{"Hi"}
    fmt.Println(w)
}
Run Code Online (Sandbox Code Playgroud)

输出:

Stringer called.
Run Code Online (Sandbox Code Playgroud)

规范:输入 Stringer,并查看源代码

type Stringer interface {
        String() string
}
Run Code Online (Sandbox Code Playgroud)

Stringer 由任何具有 String 方法的值实现,该方法定义了该值的“本机”格式。String 方法用于打印作为操作数传递给任何接受字符串的格式或无格式打印机(如 Print)的值。


另见:

为什么我不能将 *Struct 分配给 *Interface?
具有嵌入式匿名接口的结构的含义?
嵌入式接口
Golang:当你有多重继承时,接口有什么意义