接口{}是什么意思?

我是接口方面的新手,并尝试通过 Github执行 SOAP 请求

我不明白

Msg interface{}

在这个代码中:

type Envelope struct {
Body `xml:"soap:"`
}


type Body struct {
Msg interface{}
}

我观察到了同样的句法

fmt.Println

但不知道他们的目的是什么

interface{}
66236 次浏览

它被称为 空白界面,由所有类型实现,这意味着您可以将任何内容放在 Msg字段中。

例如:

body := Body{3}
fmt.Printf("%#v\n", body) // -> main.Body{Msg:3}


body = Body{"anything"}
fmt.Printf("%#v\n", body) // -> main.Body{Msg:"anything"}


body = Body{body}
fmt.Printf("%#v\n", body) // -> main.Body{Msg:main.Body{Msg:"anything"}}

这是类型一旦拥有接口的所有方法就实现接口这一事实的逻辑扩展。

注: Go 1.18(Q12022)确实将 interface{}重命名为 any(别名为 interface{})。
参见 第49884期CL 368254提交2580d0e
请看这个答案的最后一部分。


你可参考文章“ 如何在 Go 中使用接口”(以“ Russ Cox 对接口的描述”为基础) :

什么 接口?

界面有两个要素:

  • 它是一套方法,
  • 但它也是一种类型

interface{}类型(或者带有 Go 1.18 + 的 any) ,空白界面是没有方法的接口。

由于没有实现关键字,所有类型都至少实现了零个方法,并且自动完成了满足接口的 所有类型都满足空接口
这意味着,如果编写一个以 interface{}值作为参数的函数,则 您可以为该函数提供任何值

(这就是 Msg在你的问题中所代表的: 任何值)

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




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

让人困惑的是:

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

初学地鼠被引导去相信“ v是任何类型的”,但这是错误的。
v不是任何类型; interface{}型的

当将值传递给 DoSomething函数时,Go 运行时将执行 类型转换(如果需要)和 将值转换为 interface{}
所有值在运行时只有一种类型,而 v的一种静态类型是 interface{}(或者带有 Go 1.18 + 的 any)。

一个接口值由两个数据字 构成:

  • 一个单词用于指向值的基础类型的方法表,
  • 另一个词用来指向该值所持有的实际数据。

附录: 这是 Russ 关于接口结构的完整文章:

type Stringer interface {
String() string
}

接口值表示为两个单词对,提供一个指向存储在接口中的类型信息的指针和一个指向关联数据的指针。
将 b 赋给 Stringer 类型的接口值将设置接口值的两个单词。

http://research.swtch.com/gointer2.png

接口值中的第一个词指向我所称的接口表或 itable (发音为 i-table; 在运行时源代码中,C 实现名称为 Itab)。
这个 able 从一些关于所涉及类型的元数据开始,然后变成一个函数指针列表。
注意,itable 对应于接口类型,而不是动态类型
在我们的示例中,itable for Stringer保持类型 Binary 列出了用于满足 Stringer 的方法,它就是 String: Binary 的其他方法(Get)在 itable中没有出现。

接口值中的第二个单词指向实际数据 ,在本例中是 b的副本。
作业 var s Stringer = b制作 b的副本而不是指向 b的原因与 var c uint64 = b制作副本的原因相同: 如果 b后来发生变化,那么 sc应该具有原始值,而不是新值。
存储在接口中的值可能任意大,但只有一个单词专门用于保存接口结构中的值,因此赋值将在堆上分配一块内存,并在一个单词槽中记录指针。


第33232期 似乎指出 any是 Go 1.18(Q12022)中 interface{}的别名

拉斯 · 考克斯解释说:

  1. any”仅仅是一个约束,这个细节将出现在泛型的每一篇文章中——书籍、博客文章等等。 如果我们认为自己最终可能会允许这种做法,那么从一开始就允许这种做法是有道理的,并且可以避免使所有的书面材料失效。

  2. any”仅用于约束是一个意想不到的削减,降低了概念的一般性和正交性。 说“让我们拭目以待”很容易,但是处方的使用往往会产生比完全通用性更多的参差不齐的特征。我们在类型别名中也看到了这一点(谢天谢地,我们抵制了几乎所有提议的剪切)。

  3. 如果在泛型中允许使用“ any”,而非非泛型代码,那么它可能会鼓励人们过度使用泛型,仅仅因为“ any”比“ interface{}”更好写,而关于泛型与否的决定实际上应该考虑其他因素。

  4. 如果我们也允许‘ any’用于普通的非泛型用法,那么在代码中看到 interface{}可以作为一种信号,表明代码早于泛型,并且在后泛型世界中还没有被重新考虑。 一些使用 interface{}的代码应该使用泛型,其他代码应该继续使用接口。
    通过这样或那样的方式重写它来删除文本“ interface{}”将给人们一个清晰的方式来看看他们更新了什么和没有更新什么。(当然,出于向后兼容的原因,一些可能更适合泛型的代码仍然必须使用 interface{},但是仍然可以更新它以确认已经考虑并做出了决定。)

该线程还包括一个 关于 interface{}的解释:

这不是一个特殊的设计,而是围棋类型声明语法的蕴涵。

可以使用多于零个方法的匿名接口:

func f(a interface{Foo(); Bar()}) {
a.Foo()
a.Bar()
}

类似于如何在需要类型的任何地方使用匿名结构:

func f(a struct{Foo int; Bar string}) {
fmt.Println(a.Foo)
fmt.Println(a.Bar)
}

空接口恰好匹配所有类型,因为所有类型都至少有零个方法。

移除 interface{}意味着如果你想保持一致/不想引入特殊情况,就要从语言中移除所有的接口功能。

来自 戈兰规格:

接口类型指定称为其接口的方法集 接口类型的变量可以用方法存储任何类型的值 集合,它是接口的任何超集。这种类型被称为 未初始化的变量的值 接口类型为空。

类型实现包含其方法的任何子集的任何接口 因此可以实现几个不同的接口。例如, 所有类型都实现了空接口:

接口{}

图表的概念是:

  1. 所有东西都有 类型。你可以定义一个新的类型,让我们称之为 T。现在我们的类型 T有3个方法: ABC
  2. 为类型指定的方法集称为“ 接口类型”。让我们在示例中调用它: T _ interface。等于 T_interface = (A, B, C)
  3. 可以通过定义方法的 签名来创建“接口类型”
  4. 当您指定 类型变量为“接口类型”时,您可以只指定具有接口的类型,该接口是您的接口的超集。 这意味着包含在 MyInterface中的所有方法都必须包含在 T_interface

您可以推断,所有类型的所有“接口类型”都是空接口的超集。

interface{}意味着您可以放置任何类型的值,包括您自己的自定义类型。Go 中的所有类型都满足一个空接口(interface{}是一个空接口)。
在您的示例中,Msg 字段可以具有任何类型的值。

例如:

package main


import (
"fmt"
)


type Body struct {
Msg interface{}
}


func main() {
b := Body{}
b.Msg = "5"
fmt.Printf("%#v %T \n", b.Msg, b.Msg) // Output: "5" string
b.Msg = 5


fmt.Printf("%#v %T", b.Msg, b.Msg) //Output:  5 int
}

去游乐场

这里已经有了很好的答案,让我也为那些想要凭直觉理解它的人加上我自己的答案:


接口

这是一个只有一种方法的接口:

type Runner interface {
Run()
}

因此,任何具有 Run()方法的类型都满足 Runner 接口:

type Program struct {
/* fields */
}


func (p Program) Run() {
/* running */
}


func (p Program) Stop() {
/* stopping */
}
  • 虽然 Program 类型也有 Stop 方法,但它仍然满足 Runner 接口,因为所需要的只是具有接口的所有方法来满足它。

  • 因此,它有一个 Run 方法,并且满足 Runner 接口。


空接口

下面是一个没有任何方法的命名空接口:

type Empty interface {
/* it has no methods */
}

因此任何类型都满足这个接口,因为不需要任何方法来满足这个接口,例如:

// Because, Empty interface has no methods, following types satisfy the Empty interface
var a Empty


a = 5
a = 6.5
a = "hello"

但是,上面的程序类型满足要求吗? 是的:

a = Program{} // ok

接口{}等于上面的空接口。

var b interface{}


// true: a == b


b = a
b = 9
b = "bye"

如你所见,它没有什么神秘的,但它很容易被滥用。尽量远离它。


Https://play.golang.org/p/a-vwtddwj7g

这个例子扩展了@VonC 的出色回答和@NickCraig-Wood 的评论。interface{}可以指向任何内容,您需要一个强制转换/类型断言来使用它。

package main


import (
. "fmt"
"strconv"
)


var c = cat("Fish")
var d = dog("Bone")


func main() {
var i interface{} = c
switch i.(type) {
case cat:
c.Eat() // Fish
}


i = d
switch i.(type) {
case dog:
d.Eat() // Bone
}


i = "4.3"
Printf("%T %v\n", i, i) // string 4.3
s, _ := i.(string)      // type assertion
f, _ := strconv.ParseFloat(s, 64)
n := int(f)             // type conversion
Printf("%T %v\n", n, n) // int 4
}


type cat string
type dog string
func (c cat) Eat() { Println(c) }
func (d dog) Eat() { Println(d) }

i是一个值为 cat("Fish")的空接口变量。从接口类型的值创建方法值是合法的。见 https://golang.org/ref/spec#Interface_types

类型开关确认 i接口类型为 cat("Fish")。见 https://golang.org/doc/effective_go.html#type_switch。然后,i被重新分配到 dog("Bone")。类型开关确认 i接口的类型已更改为 dog("Bone")

您还可以通过尝试赋值: var _ I = T{}来要求编译器检查 T类型是否实现了接口 I。参见 https://golang.org/doc/faq#guarantee_satisfies_interfacehttps://stackoverflow.com/a/60663003/12817546

所有类型都实现空接口 interface{}。参见 i0和 i1。在这个例子中,i被重新分配,这次是一个字符串“4.3”。然后,在 s使用 strconv转换为 float64类型的 f之前,i被分配给具有 i.(string)的新字符串变量 s。最后,f被转换为等于4的 int 类型的 n。见 i2

Go 的内置映射和切片,加上使用空接口构造容器的能力(显式拆箱) ,意味着在许多情况下可以编写代码来实现泛型所能实现的功能(如果不那么顺利的话)。参见 https://golang.org/doc/faq#generics

接口是在编译时未知的类型

它是对象和结构类型之间的契约,以满足通用功能 或者 作用于不同类型结构对象的公共功能 例如,在下面的代码中,PrintDetails 是作用于不同类型的结构的一个常见功能,如 Engineering、 Manager、, 前辈 请找到示例代码 接口示例 < a href = “ https://play.golang.org/p/QnAqEYGiiF7”rel = “ nofollow norefrer”> https://play.golang.org/p/qnaqeygiif7

  • 方法可以在 GO 中绑定到 任何类型(int、 string、指针等)

  • 接口是一种声明 方法一种类型应该具有什么的方法,只要 A 类型已经实现了那些方法,就可以将这些方法分配给该接口。

  • 接口{}只有 不清楚方法,因此它可以接受 < strong > 任何类型