Quelle est la signification de l'interface {}?


133

Je suis nouveau dans les interfaces et j'essaie de faire une requête SOAP par github

Je ne comprends pas la signification de

Msg interface{}

dans ce code:

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

type Body struct {
    Msg interface{}
}

J'ai observé la même syntaxe dans

fmt.Println

mais ne comprends pas ce qui est réalisé par

interface{}

20
interface{}est plus ou moins l'équivalent de void *in C. Il peut pointer vers n'importe quoi et vous avez besoin d'une assertion cast / type pour l'utiliser.
Nick Craig-Wood

Quelle est la signification de l'interface {}? Voir stackoverflow.com/a/62337836/12817546 .
Tom J

Réponses:


189

Vous pouvez vous référer à l'article " Comment utiliser les interfaces dans Go " (basé sur la " description des interfaces de Russ Cox "):

Qu'est - ce qu'une interface?

Une interface est deux choses:

  • c'est un ensemble de méthodes,
  • mais c'est aussi un type

Le interface{}type, l' interface vide est l'interface qui n'a pas de méthodes.

Puisqu'il n'y a pas de mot-clé implements, tous les types implémentent au moins zéro méthode, et la satisfaction d'une interface se fait automatiquement, tous les types satisfont l'interface vide .
Cela signifie que si vous écrivez une fonction qui prend une interface{}valeur comme paramètre, vous pouvez fournir à cette fonction n'importe quelle valeur .

(C'est ce que Msgreprésente dans votre question: toute valeur)

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

Voici où cela devient déroutant:

à l'intérieur de la DoSomethingfonction, quel est vle type de s?

Les gaufres débutants sont amenés à croire que « vest de n'importe quel type», mais c'est faux.
vn'est d'aucun type; c'est de interface{}type .

Lors de la transmission d'une valeur dans la DoSomethingfonction, le moteur d'exécution Go effectuera une conversion de type (si nécessaire) et convertira la valeur en interface{}valeur .
Toutes les valeurs ont exactement un type à l'exécution, et vle seul type statique est interface{}.

Une valeur d'interface est constituée de deux mots de données :

  • un mot est utilisé pour pointer vers une table de méthodes pour le type sous-jacent de la valeur,
  • et l'autre mot est utilisé pour désigner les données réelles détenues par cette valeur.

Addendum: Voici où l'article de Russ est assez complet concernant une structure d'interface:

type Stringer interface {
    String() string
}

Les valeurs d'interface sont représentées sous la forme d'une paire de deux mots donnant un pointeur vers des informations sur le type stocké dans l'interface et un pointeur vers les données associées.
L'affectation de b à une valeur d'interface de type Stringer définit les deux mots de la valeur d'interface.

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

Le premier mot de la valeur de l'interface pointe vers ce que j'appelle une table d'interface ou itable (prononcé i-table; dans les sources d'exécution, le nom d'implémentation C est Itab).
L'itable commence par des métadonnées sur les types impliqués, puis devient une liste de pointeurs de fonction.
Notez que l'itable correspond au type d'interface, pas au type dynamique .
En termes de notre exemple, l'itable pour Stringercontenir le type Binary répertorie les méthodes utilisées pour satisfaire Stringer, ce qui est juste String: Les autres méthodes de Binary ( Get) n'apparaissent pas dans le itable.

Le deuxième mot de la valeur d'interface pointe vers les données réelles , dans ce cas une copie de b.
L'affectation var s Stringer = bfait une copie de bplutôt que de pointer bpour la même raison qui var c uint64 = bfait une copie: si les bmodifications ultérieures, set csont censées avoir la valeur d'origine, pas la nouvelle.
Les valeurs stockées dans les interfaces peuvent être arbitrairement grandes, mais un seul mot est dédié à la conservation de la valeur dans la structure d'interface, de sorte que l'affectation alloue un morceau de mémoire sur le tas et enregistre le pointeur dans l'emplacement d'un mot.


4
Qu'entendez-vous par «deux mots de données»? Plus précisément, que signifie le «mot»?
Mingyu

3
@Mingyu J'ai complété la réponse pour illustrer ces deux mots (points 32 bits).
VonC le

2
@Mingyu: VonC fait référence à un mot au sens de l'architecture informatique - une collection de bits qui définissent une donnée de taille fixe. La taille des mots est régie par l'architecture de processeur que vous utilisez.
Dan Esparza

1
merci @VonC pour votre réponse ... le vrai est que je suis fatigué d'être descendu quand je demande des choses ... les gens me disent la plupart du temps que je devrais lire la documentation ... je me souviendrai de votre suggestion si je me sens avec volonté d'écrire correctement un article pour cela ... mais je ne peux vraiment pas penser à une autre façon de demander. Alors merci quand même et excusez ma faible volonté. Vous êtes invités à jeter un œil à ceci: stackoverflow.com/questions/45577301/… pour clarifier pourquoi je n'aime pas demander.
Victor

1
@vic pas de problème, et désolé pour votre mauvaise expérience précédente en tant que demandeur. C'est juste que les commentaires ne conviennent pas aux questions et réponses.
VonC

34

interface{}signifie que vous pouvez mettre une valeur de n'importe quel type, y compris votre propre type personnalisé. Tous les types de Go satisfont une interface vide ( interface{}c'est une interface vide).
Dans votre exemple, le champ Msg peut avoir une valeur de n'importe quel type.

Exemple:

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
}

Aller au terrain de jeu


12

Elle s'appelle l' interface vide et est implémentée par tous les types, ce qui signifie que vous pouvez tout mettre dans le Msgchamp.

Exemple :

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

C'est l'extension logique du fait qu'un type implémente une interface dès qu'il a toutes les méthodes de l'interface.


signifie qu'il pourrait s'agir d'un int à la structure définie par l'utilisateur ??
utilisateur

11

Il y a déjà de bonnes réponses ici. Permettez-moi d'ajouter le mien aussi pour ceux qui veulent le comprendre intuitivement:


Interface

Voici une interface avec une méthode:

type Runner interface {
    Run()
}

Donc, tout type qui a une Run()méthode satisfait l'interface Runner:

type Program struct {
    /* fields */
}

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

func (p Program) Stop() {
    /* stopping */
}
  • Bien que le type Program ait également une méthode Stop, il satisfait toujours l'interface Runner car il suffit d'avoir toutes les méthodes d'une interface pour la satisfaire.

  • Donc, il a une méthode Run et il satisfait l'interface Runner.


Interface vide

Voici une interface vide nommée sans aucune méthode:

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

Donc, tout type satisfait cette interface. Parce qu'aucune méthode n'est nécessaire pour satisfaire cette interface. Par exemple:

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

a = 5
a = 6.5
a = "hello"

Mais, le type de programme ci-dessus le satisfait-il? Oui:

a = Program{} // ok

interface {} est égal à l'interface vide ci-dessus.

var b interface{}

// true: a == b

b = a
b = 9
b = "bye"

Comme vous le voyez, cela n'a rien de mystérieux, mais il est très facile d'en abuser. Éloignez-vous autant que vous le pouvez.


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


Le type Runner interfacen'est pas utilisé dans l'exemple de terrain de jeu Go.
Tom J

9

D'après les spécifications de Golang :

Un type d'interface spécifie un ensemble de méthodes appelé son interface. Une variable de type interface peut stocker une valeur de n'importe quel type avec un ensemble de méthodes qui est n'importe quel sur-ensemble de l'interface. On dit qu'un tel type met en œuvre l'interface. La valeur d'une variable non initialisée de type interface est nulle.

Un type implémente toute interface comprenant n'importe quel sous-ensemble de ses méthodes et peut donc implémenter plusieurs interfaces distinctes. Par exemple, tous les types implémentent l'interface vide:

interface{}

Les concepts aux graps sont:

  1. Tout a un type . Vous pouvez définir un nouveau type, Appelons-le dire de T. Soit maintenant notre type Ta 3 méthodes: A, B, C.
  2. L'ensemble des méthodes spécifiées pour un type est appelé le " type d'interface ". Appelons-le dans notre exemple: T_interface. Est égal àT_interface = (A, B, C)
  3. Vous pouvez créer un "type d'interface" en définissant la signature des méthodes.MyInterface = (A, )
  4. Lorsque vous spécifiez une variable de type "type d'interface", vous ne pouvez lui affecter que des types qui ont une interface qui est un sur-ensemble de votre interface. Cela signifie que toutes les méthodes contenues dans MyInterfacedoivent être contenues à l'intérieurT_interface

Vous pouvez en déduire que tous les "types d'interface" de tous les types sont un sur-ensemble de l'interface vide.


1

Un exemple qui prolonge l'excellente réponse de @VonC et le commentaire de @ NickCraig-Wood. interface{}peut pointer vers n'importe quoi et vous avez besoin d'une assertion de cast / type pour l'utiliser.

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

iest une variable d'une interface vide avec une valeur cat("Fish"). Il est légal de créer une valeur de méthode à partir d'une valeur de type d'interface. Voir https://golang.org/ref/spec#Interface_types .

Un commutateur de type confirme que le itype d'interface est cat("Fish"). Voir https://golang.org/doc/effective_go.html#type_switch . iest ensuite réaffecté à dog("Bone"). Un commutateur de type confirme que ile type d'interface est devenu dog("Bone").

Vous pouvez également demander au compilateur de vérifier que le type Timplémente l'interface Ien essayant une mission: var _ I = T{}. Voir https://golang.org/doc/faq#guarantee_satisfies_interface et https://stackoverflow.com/a/60663003/12817546 .

Tous les types implémentent l'interface vide interface{}. Voir https://talks.golang.org/2012/goforc.slide#44 et https://golang.org/ref/spec#Interface_types . Dans cet exemple, iest réaffecté, cette fois à une chaîne "4.3". iest ensuite assigné à une nouvelle variable de chaîne savec i.(string)before sest converti en un type float64 en futilisant strconv. Enfin fest converti en nun type int égal à 4. Voir Quelle est la différence entre la conversion de type et l'assertion de type?

Les cartes et les tranches intégrées de Go, ainsi que la possibilité d'utiliser l'interface vide pour construire des conteneurs (avec unboxing explicite) signifient que dans de nombreux cas, il est possible d'écrire du code qui fait ce que les génériques permettraient, bien que moins facilement. Voir https://golang.org/doc/faq#generics .


Découplez le code avec une interface. Voir stackoverflow.com/a/62297796/12817546 . Appelez une méthode «dynamiquement». Voir stackoverflow.com/a/62336440/12817546 . Accédez à un package Go. Voir stackoverflow.com/a/62278078/12817546 . Attribuez n'importe quelle valeur à une variable. Voir stackoverflow.com/a/62337836/12817546 .
Tom J
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.