What's the meaning of interface{}?

huangapple go评论96阅读模式
英文:

What's the meaning of interface{}?

问题

我是你的中文翻译助手,我会帮你翻译英文内容。以下是你要翻译的内容:

我对接口还不熟悉,正在尝试通过github进行SOAP请求。

我不理解这段代码中的含义:

Msg interface{}

在这段代码中,我观察到了相同的语法:

fmt.Println

但我不明白通过

interface{}

实现了什么目的。

英文:

I'm new to interfaces and trying to do SOAP request by github

I don't understand the meaning of

Msg interface{}

in this code:

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

type Body struct {
	Msg interface{}
}

I've observed the same syntax in

fmt.Println

but don't understand what's being achieved by

interface{}

答案1

得分: 235

注意:Go 1.18(2022年第一季度)将interface{}重命名为anyinterface{}的别名)。
参见问题49884CL 368254commit 2580d0e
请参阅答案的最后一部分。


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

接口是什么?

接口是两个东西:

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

**interface{}**类型(或者在Go 1.18+中是any),空接口是没有方法的接口。

由于没有implements关键字,所有类型至少实现零个方法,并且自动满足接口,所有类型都满足空接口
这意味着如果你编写一个以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的接口值会设置接口值的两个字。

What's the meaning of interface{}?

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

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


问题33232似乎指出在Go 1.18(2022年第一季度)中anyinterface{}的别名。

Russ Cox解释

  1. any”仅用于约束是一个细节,将出现在泛型的所有写作中-书籍、博客文章等等。
    如果我们认为最终可能允许它,那么从一开始就允许它并避免使所有已写的材料无效是有意义的。

  2. any”仅用于约束是一个意外的例外,它减少了概念的通用性和正交性。
    很容易说“让我们等着瞧”,但是规定使用往往会产生比完全通用性更多的不连贯特性。我们在类型别名方面也看到了这一点(幸运的是,我们几乎抵制了所有提出的例外)。

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

  4. 如果我们也允许在普通的非泛型用法中使用“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{},那么如果要保持一致性/不引入特殊情况,就意味着从语言中删除所有接口功能。

英文:

Note: Go 1.18 (Q1 2022) does rename interface{} to any (alias for interface{}).
See issue 49884, CL 368254 and commit 2580d0e.
See the last part of this answer.


You can refer to the article "How to use interfaces in Go" (based on "Russ Cox’s description of interfaces"):

> What is an interface?
>
> An interface is two things:
>
> - it is a set of methods,
>- but it is also a type
>
> The interface{} type (or any with Go 1.18+), the empty interface is the interface that has no methods.
>
> Since there is no implements keyword, all types implement at least zero methods, and satisfying an interface is done automatically, all types satisfy the empty interface.
That means that if you write a function that takes an interface{} value as a parameter, you can supply that function with any value.

(That is what Msg represents in your question: any value)

<!-- language-all: go -->

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


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

> Here’s where it gets confusing:
>
> inside of the DoSomething function, what is v's type?
>
> Beginner gophers are led to believe that “v is of any type”, but that is wrong.
v is not of any type; it is of interface{} type.
>
> When passing a value into the DoSomething function, the Go runtime will perform a type conversion (if necessary), and convert the value to an interface{} value.
All values have exactly one type at runtime, and v's one static type is interface{} (or any with Go 1.18+).
>
> An interface value is constructed of two words of data:
>
> - one word is used to point to a method table for the value’s underlying type,
>- and the other word is used to point to the actual data being held by that value.


Addendum: This is were Russ's article is quite complete regarding an interface structure:

type Stringer interface {
    String() string
}

> Interface values are represented as a two-word pair giving a pointer to information about the type stored in the interface and a pointer to the associated data.
Assigning b to an interface value of type Stringer sets both words of the interface value.
>
> What's the meaning of interface{}?
>
> The first word in the interface value points at what I call an interface table or itable (pronounced i-table; in the runtime sources, the C implementation name is Itab).
The itable begins with some metadata about the types involved and then becomes a list of function pointers.
Note that the itable corresponds to the interface type, not the dynamic type.
In terms of our example, the itable for Stringer holding type Binary lists the methods used to satisfy Stringer, which is just String: Binary's other methods (Get) make no appearance in the itable.
>
> The second word in the interface value points at the actual data, in this case a copy of b.
The assignment var s Stringer = b makes a copy of b rather than point at b for the same reason that var c uint64 = b makes a copy: if b later changes, s and c are supposed to have the original value, not the new one.
Values stored in interfaces might be arbitrarily large, but only one word is dedicated to holding the value in the interface structure, so the assignment allocates a chunk of memory on the heap and records the pointer in the one-word slot.


Issue 33232 seems to point out to any as an alias to interface{} in Go 1.18 (Q1 2022)

Russ Cox explains:

> 1. 'any' being only for constraints is a detail that will be in every writeup of generics - books, blog posts, and so on.
If we think we are likely to allow it eventually, it makes sense to allow it from the start and avoid invalidating all that written material.
>
> 1. 'any' being only for constraints is an unexpected cut-out that reduces generality and orthogonality of concepts.
It's easy to say "let's just wait and see", but prescribing uses tends to create much more jagged features than full generality. We saw this with type aliases as well (and resisted almost all the proposed cut-outs, thankfully).
>
> 1. If 'any' is allowed in generics but not non-generic code, then it might encourage people to overuse generics simply because 'any' is nicer to write than 'interface{}', when the decision about generics or not should really be made by considering other factors.
>
> 1. If we allow 'any' for ordinary non-generic usage too, then seeing interface{} in code could serve as a kind of signal that the code predates generics and has not yet been reconsidered in the post-generics world.
Some code using interface{} should use generics. Other code should continue to use interfaces.
Rewriting it one way or another to remove the text 'interface{}' would give people a clear way to see what they'd updated and hadn't. (Of course, some code that might be better with generics must still use interface{} for backwards-compatibility reasons, but it can still be updated to confirm that the decision was considered and made.)

That thread also includes an explanation about interface{}:

>It's not a special design, but a logical consequence of Go's type declaration syntax.
>
>You can use anonymous interfaces with more than zero methods:
>go
&gt;func f(a interface{Foo(); Bar()}) {
&gt; a.Foo()
&gt; a.Bar()
&gt;}
&gt;

>Analogous to how you can use anonymous structs anywhere a type is expected:
>
>go
&gt;func f(a struct{Foo int; Bar string}) {
&gt; fmt.Println(a.Foo)
&gt; fmt.Println(a.Bar)
&gt;}
&gt;

>
> An empty interface just happens to match all types because all types have at least zero methods.
>
> Removing interface{} would mean removing all interface functionality from the language if you want to stay consistent / don't want to introduce a special case.

答案2

得分: 43

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) // 输出:"5" string
	b.Msg = 5

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

Go Playground

英文:

interface{} means you can put value of any type, including your own custom type. All types in Go satisfy an empty interface (interface{} is an empty interface).
In your example, Msg field can have value of any type.

Example:

package main

import (
	&quot;fmt&quot;
)

type Body struct {
	Msg interface{}
}

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

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

Go Playground

答案3

得分: 17

这里已经有了很好的答案。让我也为其他想直观理解的人添加一些内容:


接口

这是一个具有一个方法的接口:

type Runner interface {
    Run()
}

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

type Program struct {
    /* 字段 */
}

func (p Program) Run() {
    /* 运行 */
}

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

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


空接口

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

type Empty interface {
    /* 它没有任何方法 */
}

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

// 因为Empty接口没有方法,以下类型都满足Empty接口
var a Empty

a = 5
a = 6.5
a = "hello"

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

a = Program{} // 可行

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

var b interface{}

// true: a == b

b = a
b = 9
b = "bye"

正如你所看到的,这没有什么神秘的,但很容易被滥用。尽量避免使用它。


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

英文:

There are already good answers here. Let me add my own too for others who want to understand it intuitively:


Interface

Here's an interface with one method:

type Runner interface {
    Run()
}

So any type that has a Run() method satisfies the Runner interface:

type Program struct {
    /* fields */
}

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

func (p Program) Stop() {
    /* stopping */
}
  • Although the Program type has also a Stop method, it still satisfies the Runner interface because all that is needed is to have all of the methods of an interface to satisfy it.

  • So, it has a Run method and it satisfies the Runner interface.


Empty Interface

Here's a named empty interface without any methods:

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

So any type satisfies this interface. Because, no method is needed to satisfy this interface. For example:

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

a = 5
a = 6.5
a = &quot;hello&quot;

But, does the Program type above satisfy it? Yes:

a = Program{} // ok

interface{} is equal to the Empty interface above.

var b interface{}

// true: a == b

b = a
b = 9
b = &quot;bye&quot;

As you see, there's nothing mysterious about it but it's very easy to abuse. Stay away from it as much as you can.


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

答案4

得分: 13

这被称为空接口,它由所有类型实现,这意味着你可以将任何东西放入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"}}

这是一个逻辑扩展,即只要一个类型具有接口的所有方法,它就实现了该接口。

英文:

It's called the empty interface and is implemented by all types, which means you can put anything in the Msg field.

Example :

body := Body{3}
fmt.Printf(&quot;%#v\n&quot;, body) // -&gt; main.Body{Msg:3}

body = Body{&quot;anything&quot;}
fmt.Printf(&quot;%#v\n&quot;, body) // -&gt; main.Body{Msg:&quot;anything&quot;}

body = Body{body}
fmt.Printf(&quot;%#v\n&quot;, body) // -&gt; main.Body{Msg:main.Body{Msg:&quot;anything&quot;}}

This is the logical extension of the fact that a type implements an interface as soon as it has all methods of the interface.

答案5

得分: 10

根据Golang规范

接口类型指定了一个称为其接口的方法集。接口类型的变量可以存储具有方法集的任何类型的值,该方法集是接口的任何超集。这样的类型被称为实现了该接口。未初始化的接口类型变量的值为nil。

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

interface{}

需要理解的概念有:

  1. 每个东西都有一个__类型__。你可以定义一个新的类型,我们称之为T。假设现在我们的类型T有3个方法:ABC
  2. 为类型指定的方法集被称为"接口类型"。在我们的例子中,我们称之为T_interface。它等于T_interface = (A, B, C)
  3. 你可以通过定义方法的__签名__来创建一个"接口类型"。MyInterface = (A, )
  4. 当你指定一个类型为"接口类型"的变量时,你只能将具有接口是你接口的超集的类型赋值给它。这意味着MyInterface中包含的所有方法必须包含在T_interface中。

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

英文:

From the Golang Specifications:

> An interface type specifies a method set called its interface. A
> variable of interface type can store a value of any type with a method
> set that is any superset of the interface. Such a type is said to
> implement the interface. The value of an uninitialized variable of
> interface type is nil.
>
> A type implements any interface comprising any subset of its methods
> and may therefore implement several distinct interfaces. For instance,
> all types implement the empty interface:
>
> interface{}

The concepts to graps are:

  1. Everything has a Type. You can define a new type, let's call it T. Let's say now our Type T has 3 methods: A, B, C.
  2. The set of methods specified for a type is called the "interface type". Let's call it in our example: T_interface. Is equal to T_interface = (A, B, C)
  3. You can create an "interface type" by defining the signature of the methods. MyInterface = (A, )
  4. When you specify a variable of type, "interface type", you can assign to it only types which have an interface that is a superset of your interface.
    That means that all the methods contained in MyInterface have to be contained inside T_interface

You can deduce that all the "interface types" of all the types are a superset of the empty interface.

答案6

得分: 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)      // 类型断言
	f, _ := strconv.ParseFloat(s, 64)
	n := int(f)             // 类型转换
	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_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)进行类型断言,然后s被转换为float64类型的f,使用strconv。最后,f被转换为int类型的n,其值为4。参见https://stackoverflow.com/questions/20494229/what-is-the-difference-between-type-conversion-and-type-assertion。

Go的内置映射和切片,以及使用空接口构建容器(需要显式拆箱)的能力,意味着在许多情况下,可以编写能够实现泛型功能的代码,尽管不够灵活。参见https://golang.org/doc/faq#generics。

英文:

An example that extends the excellent answer by @VonC and the comment by @NickCraig-Wood. interface{} can point to anything and you need a cast/type assertion to use it.

package main

import (
	. &quot;fmt&quot;
	&quot;strconv&quot;
)

var c = cat(&quot;Fish&quot;)
var d = dog(&quot;Bone&quot;)

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 = &quot;4.3&quot;
	Printf(&quot;%T %v\n&quot;, i, i) // string 4.3
	s, _ := i.(string)      // type assertion
	f, _ := strconv.ParseFloat(s, 64)
	n := int(f)             // type conversion
	Printf(&quot;%T %v\n&quot;, n, n) // int 4
}

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

i is a variable of an empty interface with a value cat(&quot;Fish&quot;). It is legal to create a method value from a value of interface type. See https://golang.org/ref/spec#Interface_types.

A type switch confirms i interface type is cat(&quot;Fish&quot;) . See https://golang.org/doc/effective_go.html#type_switch. i is then reassigned to dog(&quot;Bone&quot;). A type switch confirms that i interface’s type has changed to dog(&quot;Bone&quot;) .

You can also ask the compiler to check that the type T implements the interface I by attempting an assignment: var _ I = T{}. See https://golang.org/doc/faq#guarantee_satisfies_interface and https://stackoverflow.com/a/60663003/12817546.

All types implement the empty interface interface{}. See https://talks.golang.org/2012/goforc.slide#44 and https://golang.org/ref/spec#Interface_types . In this example, i is reassigned, this time to a string "4.3".i is then assigned to a new string variable s with i.(string) before s is converted to a float64 type f using strconv. Finally f is converted to n an int type equal to 4. See https://stackoverflow.com/questions/20494229/what-is-the-difference-between-type-conversion-and-type-assertion

Go's built-in maps and slices, plus the ability to use the empty interface to construct containers (with explicit unboxing) mean in many cases it is possible to write code that does what generics would enable, if less smoothly. See https://golang.org/doc/faq#generics.

答案7

得分: 0

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

它是对象和结构类型之间的契约,用于满足共同功能
或者
在不同类型的结构对象上执行共同功能
例如,在下面的代码中,PrintDetails是在不同类型的结构体(如Engineer、Manager、Seniorhead)上执行的共同功能。
请查看以下示例代码:
interface example

英文:

Interface is a type which is unknown at compile time

It is a contract between object and the struct type to satisfy with common functionality
or
common functionality acting on different types of struct objects
for example in the below code PrintDetails is a common functionality acting on different types of structs as Engineer,Manager,
Seniorhead
please find the example code
interface examplehttps://play.golang.org/p/QnAqEYGiiF7

答案8

得分: -1

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

  • 接口是一种声明某个类型应该具有哪些方法的方式,只要某个类型实现了这些方法,就可以将其分配给该接口。

  • Interface{} 没有声明任何方法,因此它可以接受任何类型。

英文:
  • A method can bind to any type(int, string, pointer, and so on) in GO

  • Interface is a way of declear what method one type should have, as long as A type has implement those methods, this can be assigned to this interface.

  • Interface{} just has no declear of method, so it can accept any type

huangapple
  • 本文由 发表于 2014年4月18日 14:39:23
  • 转载请务必保留本文链接:https://go.coder-hub.com/23148812.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定