Go语言 interface{}是什么意思?

e37o9pze  于 5个月前  发布在  Go
关注(0)|答案(8)|浏览(37)

我是新的接口和尝试做SOAP请求的github
我不明白

Msg interface{}

字符串
在这段代码中:

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

type Body struct {
    Msg interface{}
}


我观察到同样的语法,

fmt.Println


但是不明白

interface{}

yvfmudvl

yvfmudvl1#

注意:Go 1.18(Q1 2022)确实将interface{}重命名为anyinterface{}的别名)。
参见issue 49884CL 368254commit 2580d0e
请看答案的最后一部分。
您可以参考文章“How to use interfaces in Go“(基于“Russ Cox’s description of interfaces“):
什么是接口?
接口是两件事:

  • 它是一套方法,
  • 但它也是一种
    **interface{}**类型(或Go 1.18+的any),空接口是没有方法的接口。

由于没有implements关键字,所有类型至少实现零个方法,并且满足接口是自动完成的,因此所有类型都满足空接口
这意味着,如果你写一个函数,把一个interface{}值作为参数,你可以为这个函数提供任何值
(That 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类型的接口值,将设置接口值的两个字。
x1c 0d1x的数据

interface值中的第一个单词指向我称之为接口表或itable(发音为i-table;在运行时源代码中,C实现名称为Itab)。

itable以一些关于所涉及类型的元数据开始,然后变成函数指针列表。

注意,itable对应的是接口类型,而不是动态类型

在我们的例子中,itable for Stringer holding type Binary列出了用于满足Stringer的方法,它只是String:Binary的其他方法(Get)在itable中没有出现。

接口值中的第二个字指向实际数据,在本例中是b的副本。

赋值var s Stringer = b复制了b而不是指向b,其原因与var c uint64 = b复制的原因相同:如果b后来发生变化,sc应该具有原始值,而不是新值。
存储在接口中的值可以是任意大的,但是只有一个字专用于保存接口结构中的值,因此赋值操作在堆上分配一个内存块,并将指针记录在一个字的槽中。
Issue 33232似乎指出any是Go 1.18(Q1 2022)中interface{}的别名。
拉斯考克斯解释说:
1.'any'只用于约束是每一个泛型的详细说明-书籍,博客文章等等。如果我们认为我们最终可能会允许它,那么从一开始就允许它并避免使所有书面材料无效是有意义的。
1.'any'仅用于约束,这是一个意想不到的剪切,它降低了概念的通用性和正交性。说“让我们拭目以待”很容易,但规定用途往往会创建比完全通用性更多的锯齿状特征。我们在类型别名中也看到了这一点(谢天谢地,几乎抵制了所有提议的剪切)。
1.如果'any'在泛型中被允许,但在非泛型代码中却不被允许,那么它可能会鼓励人们过度使用泛型,仅仅因为'any'比'interface{}'更好写,而决定是否使用泛型真的应该通过考虑其他因素来做出。
1.如果我们也允许“any”用于普通的非泛型用途,那么在代码中看到interface{}可以作为一种信号,表明代码早于泛型,并且尚未在后泛型世界中重新考虑。一些使用interface{}的代码应该使用泛型。其他代码应该继续使用接口。
通过某种方式重写它以删除文本“interface{}”将给给予人们一种清晰的方式来查看他们已经更新和没有更新的内容。(当然,出于向后兼容性的原因,一些可能更好地使用泛型的代码仍然必须使用interface{},但它仍然可以被更新以确认该决定是经过考虑和做出的。)
该线程还包括关于interface{}的解释:
这不是一个特殊的设计,而是Go语言类型声明语法的逻辑结果。
你可以使用匿名接口和多于零个方法:

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{}意味着删除语言中的所有接口功能,如果你想保持一致/不想引入特殊情况。

l2osamch

l2osamch2#

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
}

字符串
Go Playground

4urapxun

4urapxun3#

这里已经有了很好的答案。让我为那些想直观地理解它的人添加我自己的答案:

接口

这里有一个接口,只有一个方法:

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"

但是,上面的Program类型是否满足?是:

a = Program{} // ok

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

var b interface{}

// true: a == b

b = a
b = 9
b = "bye"


如你所见,它没有什么神秘之处,但很容易被滥用。尽可能远离它。
https://play.golang.org/p/A-vwTddWJ7G

avkwfej4

avkwfej44#

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

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

uz75evzq

uz75evzq5#

关于Golang Specifications
一个接口类型指定了一个称为接口的方法集。接口类型的变量可以存储任何类型的值,方法集是接口的任何超集。这样的类型被称为实现接口。未初始化的接口类型变量的值是nil。
一个类型实现了任何接口,包括它的方法的任何子集,因此可以实现几个不同的接口。例如,所有类型都实现了空接口:
接口{}
graps的概念是:
1.任何东西都有一个Type。你可以定义一个新的类型,让我们称之为T。假设我们的Type T有3个方法:ABC
1.为类型指定的方法集称为“interface type"。让我们在示例中称之为:T_interface. Is equal to T_interface = (A, B, C)
1.您可以通过定义方法的签名来创建“接口类型”。MyInterface = (A, )
1.当您指定 type变量“interface type”时,您只能将接口是您的接口的超集的类型分配给它。这意味着MyInterface中包含的所有方法都必须包含在T_interface
你可以推断出所有类型的所有“接口类型”都是空接口的超集。

sbtkgmzw

sbtkgmzw6#

一个扩展了@VonC的优秀答案和@NickCraig-Wood. interface{}的评论的例子可以指向任何东西,你需要一个强制转换/类型Assert来使用它。

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_interface和https://stackoverflow.com/a/60663003/12817546
所有类型都实现了空接口interface{}。参见https://talks.golang.org/2012/goforc.slide#44和https://golang.org/ref/spec#Interface_types。在本例中,i被重新分配,这次是字符串“4.3”然后,将. i赋值给一个新的字符串变量s,并使用i.(string),然后使用strconvs转换为float 64类型f。最后,将f转换为n,一个int类型,等于4.见What is the difference between type conversion and type assertion?
Go语言内置的map和slice,加上使用空接口来构造容器的能力(通过显式拆箱),意味着在许多情况下,它可以编写代码来实现泛型所能实现的功能,即使不那么流畅。https://golang.org/doc/faq#generics

s3fp2yjn

s3fp2yjn7#

接口是编译时未知的类型

它是对象和结构类型之间的契约,以满足共同功能或作用于不同类型结构对象的共同功能,例如在下面的代码中,PrintDetails是作用于不同类型结构的共同功能,如工程师,经理,高级主管请找到示例代码interface examplehttps://play.golang.org/p/QnAqEYGiiF7

58wvjzkj

58wvjzkj8#

  • 一个方法可以绑定到GO中的任何类型(int、string、pointer等)
  • 接口是一种方式,可以明确一个类型应该有什么方法,只要A类型已经实现了那些方法,就可以将其分配给这个接口。
  • 接口{}只是没有declear of method,所以它可以接受任何类型

相关问题