Qual é o significado da interface {}?

133

Eu sou novo em interfaces e tentando fazer solicitação SOAP pelo github

Eu não entendo o significado de

Msg interface{}

neste código:

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

type Body struct {
    Msg interface{}
}

Eu observei a mesma sintaxe em

fmt.Println

mas não entendo o que está sendo alcançado por

interface{}
do utilizador
fonte
20
interface{}é mais ou menos o equivalente void *em C. Ele pode apontar para qualquer coisa e você precisa de uma asserção de tipo / conversão para usá-lo.
Nick Craig-Wood
Qual é o significado da interface {}? Consulte stackoverflow.com/a/62337836/12817546 .
Tom J

Respostas:

189

Você pode consultar o artigo " Como usar interfaces no Go " (com base na " Descrição de interfaces de Russ Cox "):

O que é uma interface?

Uma interface é duas coisas:

  • é um conjunto de métodos,
  • mas também é um tipo

O interface{}tipo, a interface vazia é a interface que não possui métodos.

Como não há palavra-chave implementa, todos os tipos implementam métodos pelo menos zero e a satisfação de uma interface é feita automaticamente, todos os tipos atendem à interface vazia .
Isso significa que, se você escrever uma função que assume um interface{}valor como parâmetro, poderá fornecer essa função com qualquer valor .

(Isso é o que Msgrepresenta na sua pergunta: qualquer valor)

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

Aqui é onde fica confuso:

dentro da DoSomethingfunção, qual é vo tipo?

Esquilos iniciantes são levados a acreditar que " vé de qualquer tipo", mas isso está errado.
vnão é de nenhum tipo; é do interface{}tipo .

Ao passar um valor para a DoSomethingfunção, o tempo de execução Go executará uma conversão de tipo (se necessário) e o converterá em um interface{}valor .
Todos os valores têm exatamente um tipo em tempo de execução e esse vé um tipo estático interface{}.

Um valor de interface é construído com duas palavras de dados :

  • uma palavra é usada para apontar para uma tabela de métodos para o tipo subjacente do valor,
  • e a outra palavra é usada para apontar para os dados reais mantidos por esse valor.

Adendo: Este é o artigo de Russ bastante completo sobre uma estrutura de interface:

type Stringer interface {
    String() string
}

Os valores da interface são representados como um par de duas palavras, fornecendo um ponteiro para informações sobre o tipo armazenado na interface e um ponteiro para os dados associados.
Atribuir b a um valor de interface do tipo Stringer define as duas palavras do valor da interface.

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

A primeira palavra no valor da interface aponta para o que eu chamo de tabela de interface ou itable (tabela i pronunciada; nas fontes de tempo de execução, o nome da implementação em C é Itab).
A itable começa com alguns metadados sobre os tipos envolvidos e depois se torna uma lista de ponteiros de função.
Observe que o itable corresponde ao tipo de interface, não ao tipo dinâmico .
Em termos de nosso exemplo, o itable para Stringermanter o tipo Binary lista os métodos usados ​​para satisfazer Stringer, que é o seguinte String: Os outros métodos de Binary ( Get) não aparecem no itable.

A segunda palavra no valor da interface aponta para os dados reais , neste caso uma cópia de b.
A atribuição var s Stringer = bfaz uma cópia em bvez de apontar pelo bmesmo motivo que var c uint64 = bfaz uma cópia: se bmais tarde for alterada se ctiver o valor original, não o novo.
Os valores armazenados nas interfaces podem ser arbitrariamente grandes, mas apenas uma palavra é dedicada a manter o valor na estrutura da interface; portanto, a atribuição aloca um pedaço de memória na pilha e registra o ponteiro no slot de uma palavra.

VonC
fonte
4
O que você quer dizer com "duas palavras de dados"? Especificamente, o que a "palavra" significa?
Mingyu
3
@Mingyu Completei a resposta para ilustrar essas duas palavras (pontos de 32 bits).
VonC
2
@Mingyu: VonC está se referindo a uma palavra no sentido da arquitetura do computador - uma coleção de bits que define um dado de tamanho fixo. O tamanho da palavra é regido pela arquitetura do processador que você está usando.
Dan Esparza
1
obrigado @VonC pela sua resposta ... a verdade é que eu estou cansado de ficar no posto quando pergunto coisas ... as pessoas na maioria das vezes me dizem que eu deveria ler os documentos ... lembrarei sua sugestão se eu sentir vontade de escrever corretamente um post para ele ... mas eu realmente não consigo pensar em outra maneira de perguntar. Então, obrigado de qualquer maneira e desculpe minha baixa vontade. Você pode dar uma olhada nisto: stackoverflow.com/questions/45577301/… para esclarecer por que não gosto de perguntar.
Victor
1
@vic não tem problema, e desculpe pela sua má experiência anterior como solicitante. Só que os comentários não servem para perguntas e respostas.
VonC 5/17/17
34

interface{}significa que você pode colocar valor de qualquer tipo, incluindo seu próprio tipo personalizado. Todos os tipos no Go atendem a uma interface vazia ( interface{}é uma interface vazia).
No seu exemplo, o campo Msg pode ter valor de qualquer tipo.

Exemplo:

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

Minty
fonte
12

É chamada de interface vazia e é implementada por todos os tipos, o que significa que você pode colocar qualquer coisa no Msgcampo.

Exemplo:

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"}}

Essa é a extensão lógica do fato de um tipo implementar uma interface assim que tiver todos os métodos da interface.

Denys Séguret
fonte
significa que poderia ser uma estrutura definida pelo usuário?
usuário
11

Já existem boas respostas aqui. Deixe-me adicionar o meu também para outras pessoas que desejam entendê-lo intuitivamente:


Interface

Aqui está uma interface com um método:

type Runner interface {
    Run()
}

Portanto, qualquer tipo que tenha um Run()método satisfaz a interface do Runner:

type Program struct {
    /* fields */
}

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

func (p Program) Stop() {
    /* stopping */
}
  • Embora o tipo de programa também tenha um método Stop, ele ainda satisfaz a interface do Runner, pois tudo o que é necessário é ter todos os métodos de uma interface para satisfazê-lo.

  • Portanto, ele possui um método Run e satisfaz a interface Runner.


Interface vazia

Aqui está uma interface vazia nomeada sem nenhum método:

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

Portanto, qualquer tipo satisfaz essa interface. Porque, nenhum método é necessário para satisfazer essa interface. Por exemplo:

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

a = 5
a = 6.5
a = "hello"

Mas, o tipo de programa acima o satisfaz? Sim:

a = Program{} // ok

interface {} é igual à interface vazia acima.

var b interface{}

// true: a == b

b = a
b = 9
b = "bye"

Como você vê, não há nada de misterioso nisso, mas é muito fácil abusar. Fique longe disso o máximo que puder.


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

Inanc Gumus
fonte
O type Runner interfacenão é usado no exemplo do playground Go.
Tom J
9

Das especificações de Golang :

Um tipo de interface especifica um conjunto de métodos chamado de interface. Uma variável do tipo de interface pode armazenar um valor de qualquer tipo com um conjunto de métodos que seja qualquer superconjunto da interface. Diz-se que esse tipo implementa a interface. O valor de uma variável não inicializada do tipo de interface é nulo.

Um tipo implementa qualquer interface compreendendo qualquer subconjunto de seus métodos e, portanto, pode implementar várias interfaces distintas. Por exemplo, todos os tipos implementam a interface vazia:

interface{}

Os conceitos para graps são:

  1. Tudo tem um tipo . Você pode definir um novo tipo, vamos chamá-lo de T. Vamos dizer que agora nosso tipo Ttem 3 métodos: A, B, C.
  2. O conjunto de métodos especificado para um tipo é chamado de " tipo de interface ". Vamos chamá-lo em nosso exemplo: T_interface. É igual aT_interface = (A, B, C)
  3. Você pode criar um "tipo de interface" definindo a assinatura dos métodos.MyInterface = (A, )
  4. Quando você especifica uma variável do tipo , "tipo de interface", é possível atribuir a ela apenas tipos que possuem uma interface que é um superconjunto da sua interface. Isso significa que todos os métodos contidos MyInterfacedevem estar contidos dentroT_interface

Você pode deduzir que todos os "tipos de interface" de todos os tipos são um superconjunto da interface vazia.

fabrizioM
fonte
1

Um exemplo que amplia a excelente resposta de @VonC e o comentário de @ NickCraig-Wood. interface{}pode apontar para qualquer coisa e você precisa de uma declaração de elenco / tipo para usá-lo.

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é uma variável de uma interface com um valor vazio cat("Fish"). É legal criar um valor de método a partir de um valor do tipo de interface. Consulte https://golang.org/ref/spec#Interface_types .

Uma opção de tipo confirma que o itipo de interface é cat("Fish"). Consulte https://golang.org/doc/effective_go.html#type_switch . ié reatribuído para dog("Bone"). Uma opção de tipo confirma que io tipo de interface foi alterado para dog("Bone").

Você também pode pedir o compilador para verificar se o tipo Timplementa a interface Iao tentar uma atribuição: var _ I = T{}. Consulte https://golang.org/doc/faq#guarantee_satisfies_interface e https://stackoverflow.com/a/60663003/12817546 .

Todos os tipos implementam a interface vazia interface{}. Consulte https://talks.golang.org/2012/goforc.slide#44 e https://golang.org/ref/spec#Interface_types . Neste exemplo, ié reatribuído, desta vez para uma string "4.3". ié então atribuído a uma nova variável de cadeia scom i.(string)antes sé convertido em um tipo float64 fusando strconv. Finalmente, fé convertido em num tipo int igual a 4. Consulte Qual é a diferença entre conversão e asserção de tipo?

Os mapas e fatias integrados da Go, além da capacidade de usar a interface vazia para construir contêineres (com unboxing explícito), significa que em muitos casos é possível escrever um código que faça o que os genéricos permitiriam, se menos suavemente. Veja https://golang.org/doc/faq#generics .

Tom J
fonte
Desacoplar código com uma interface. Consulte stackoverflow.com/a/62297796/12817546 . Chame um método "dinamicamente". Consulte stackoverflow.com/a/62336440/12817546 . Acesse um pacote Go. Consulte stackoverflow.com/a/62278078/12817546 . Atribua qualquer valor a uma variável. Consulte stackoverflow.com/a/62337836/12817546 .
Tom J