我是接口的新手,并尝试通过github执行SOAP请求
我不明白
Msg interface{}
在此代码中:
type Envelope struct {
Body `xml:"soap:"`
}
type Body struct {
Msg interface{}
}
我已经观察到相同的语法
fmt.Println
但不知道通过什么实现
interface{}
我是接口的新手,并尝试通过github执行SOAP请求
我不明白
Msg interface{}
在此代码中:
type Envelope struct {
Body `xml:"soap:"`
}
type Body struct {
Msg interface{}
}
我已经观察到相同的语法
fmt.Println
但不知道通过什么实现
interface{}
Answers:
您可以参考“ 如何在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类型的接口值可设置接口值的两个字。
接口值中的第一个单词指向我所谓的接口表或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
应该具有原始值,而不是新值。
存储在接口中的值可能任意大,但只有一个字专用于在接口结构中保存该值,因此该分配在堆上分配了一块内存,并将指针记录在一个字槽中。
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
}
它称为空接口,并且由所有类型实现,这意味着您可以在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"
如您所见,它没有什么神秘之处,但是很容易滥用。尽可能远离它。
type Runner interface
是在转到操场示例中未被使用。
从Golang规范:
接口类型指定一个称为其接口的方法集。接口类型的变量可以使用方法集合存储任何类型的值,该方法集是接口的任何超集。据说这种类型实现了接口。接口类型的未初始化变量的值为nil。
类型实现包含其方法的任何子集的任何接口,因此可以实现几个不同的接口。例如,所有类型都实现空接口:
接口{}
要掌握的概念是:
T
有3种方法:A
,B
,C
。T_interface = (A, B, C)
MyInterface = (A, )
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
实现了接口。参见https://golang.org/doc/faq#guarantee_satisfies_interface和https://stackoverflow.com/a/60663003/12817546。I
var _ I = T{}
所有类型都实现空接口interface{}
。参见https://talks.golang.org/2012/goforc.slide#44和https://golang.org/ref/spec#Interface_types。在此示例中,这次i
被重新分配给字符串“ 4.3”。i
然后将分配给带有的新字符串变量s
,i.(string)
然后使用s
转换为float64类型。最后将其转换为等于4的int类型。请参见类型转换和类型断言之间的区别是什么?f
strconv
f
n
Go的内置映射和切片,以及使用空接口构造容器的能力(带有显式拆箱),意味着在许多情况下,编写代码的目的是使仿制药能够实现的功能(即使不太顺畅)也可以实现。请参阅https://golang.org/doc/faq#generics。
interface{}
void *
在C中几乎等同于C。它可以指向任何内容,您需要强制类型转换/类型声明才能使用它。