interface {}是什么意思?


133

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

我不明白

Msg interface{}

在此代码中:

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

type Body struct {
    Msg interface{}
}

我已经观察到相同的语法

fmt.Println

但不知道通过什么实现

interface{}

20
interface{}void *在C中几乎等同于C。它可以指向任何内容,您需要强制类型转换/类型声明才能使用它。
Nick Craig-Wood

interface {}是什么意思?请参阅stackoverflow.com/a/62337836/12817546
汤姆·J

Answers:


189

您可以参考“ 如何在Go中使用接口 ”一文(基于“ Russ Cox对接口的描述 ”):

什么接口?

接口是两件事:

  • 这是一组方法,
  • 但这也是一种

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

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

(这Msg代表您的问题:任何值)

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

这是令人困惑的地方:

在内部DoSomething功能,是什么v的类型?

导致地鼠的人相信“ v任何类型”,但这是错误的。
v不是任何类型;它是interface{}类型的

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

接口值由两个数据字构成

  • 一个单词用于指向该值的基础类型的方法表,
  • 换句话说,就是指向该值所保存的实际数据。

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

type Stringer interface {
    String() string
}

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

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

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

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


4
“数据的两个字”是什么意思?具体来说,“单词”是什么意思?
Mingyu 2014年

3
@Mingyu我已完成回答以说明这两个词(32位点)。
VonC 2014年

2
@Mingyu:VonC 在计算机体系结构意义上指的是一个单词 -一组定义固定大小的数据的位的集合。字长由您使用的处理器体系结构决定。
Dan Esparza

1
谢谢@VonC的回复...的确是,当我问问题时,我已经厌倦了低调的作法。大多数人告诉我,我应该阅读文档...如果我觉得与我在一起,我会记得你的建议会为它适当地写一个帖子...但是我真的想不出另一种方式问。因此,无论如何,谢谢,请原谅我的卑鄙。欢迎您查看以下内容:stackoverflow.com/questions/45577301/…阐明为什么我不喜欢问。
维克多

1
@vic没问题,对于您以前作为询问者的不良经历深表歉意。只是评论不适合提问和回答。
VonC

34

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

例:

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
}

去游乐场


12

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

这是一个事实,即类型拥有接口的所有方法后,便立即实现该接口,这是逻辑上的扩展。


意味着它可能是用户定义结构的整数
用户

11

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


接口

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

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


type Runner interface是在转到操场示例中未被使用。
汤姆Ĵ

9

Golang规范

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

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

接口{}

要掌握的概念是:

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

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


1

一个示例扩展了@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_switchi然后重新分配给dog("Bone")。类型开关确认i接口的类型已更改为dog("Bone")

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

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

Go的内置映射和切片,以及使用空接口构造容器的能力(带有显式拆箱),意味着在许多情况下,编写代码的目的是使仿制药能够实现的功能(即使不太顺畅)也可以实现。请参阅https://golang.org/doc/faq#generics


用接口解耦代码。请参阅stackoverflow.com/a/62297796/12817546。“动态”调用方法。请参阅stackoverflow.com/a/62336440/12817546。访问Go程序包。请参阅stackoverflow.com/a/62278078/12817546。将任何值分配给变量。请参阅stackoverflow.com/a/62337836/12817546
汤姆·J
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.