繁体   English   中英

interface{} 的含义是什么?

[英]What's the meaning of interface{}?

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

我不明白什么意思

Msg interface{}

在这段代码中:

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

type Body struct {
    Msg interface{}
}

我观察到相同的语法

fmt.Println

但不明白正在取得什么

interface{}

可以参考文章《 How to use interfaces in Go 》(基于《 Russ Cox对接口的描述》):

什么接口?

接口是两件事:

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

interface{}类型,空接口就是没有方法的接口。

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

(这就是Msg在您的问题中代表的含义:任何值)

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

这是令人困惑的地方:

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

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

当将值传递给DoSomething函数时,Go 运行时将执行类型转换(如有必要),并将该值转换为interface{}
所有值在运行时都只有一种类型,而v的一种静态类型是interface{}

接口值由两个数据字构成

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

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

type Stringer interface {
    String() string
}

接口值表示为一个两字对,给出一个指向存储在接口中的类型信息的指针和一个指向相关数据的指针。
将 b 分配给 Stringer 类型的接口值会设置接口值的两个字。

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

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

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

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
}

去游乐场

它被称为空接口,并由所有类型实现,这意味着您可以在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"}}

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

这里已经有很好的答案了。 让我也为其他想要直观理解它的人添加我自己的:


界面

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

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

interface{} 等于上面的 Empty 接口。

var b interface{}

// true: a == b

b = a
b = 9
b = "bye"

如您所见,它并没有什么神秘之处,但它很容易被滥用。 尽可能远离它。


https://play.golang.org/p/A-vwTddWJ7G

Golang 规范

接口类型指定称为其接口的方法集。 接口类型的变量可以存储任何类型的值,方法集是接口的任何超集。 据说这种类型实现了接口。 接口类型的未初始化变量的值为 nil。

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

界面{}

葡萄的概念是:

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

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

一个例子扩展了@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")

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

所有类型都实现了空接口interface{} 请参阅https://talks.golang.org/2012/goforc.slide#44https://golang.org/ref/spec#Interface_types 在这个例子中, i被重新分配,这次是一个字符串“4.3”。 然后将i分配给一个带有i.(string)的新字符串变量s ,然后使用strconvs转换为 float64 类型f 最后f转换为n一个等于 4 的 int 类型。请参阅类型转换和类型断言有什么区别?

Go 的内置映射和切片,以及使用空接口构造容器(显式拆箱)的能力意味着在许多情况下,如果不太顺利的话,可以编写执行泛型支持的代码。 请参阅https://golang.org/doc/faq#generics

接口是一种类似结构的类型,但不包含任何实现,它是对象和结构类型之间的契约,以满足通用功能或作用于不同类型结构对象的通用功能,例如在下面的代码中 PrintDetails 是一个通用功能以工程师、经理、高级主管的身份处理不同类型的结构,请找到示例代码接口示例https://play.golang.org/p/QnAqEYGiiF7

  • 一个方法可以绑定到 GO 中的任何类型(int、string、pointer 等)

  • 接口是一种明确一个类型应该有什么方法的方式,只要一个类型实现了这些方法,就可以将this赋值给这个接口。

  • Interface{} 只是没有声明 method ,所以它可以接受任何类型

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM