L'interface Go Empty expliquée

Interfaces, interface {}, conversions

Une interface dans Go spécifie unensemble de méthodes. Tout type qui implémente cet ensemble de méthodes est ditimplémenter l'interface.

Une interface est implémentée implicitement. Il n'y a pasmet en oeuvremot-clé, utilisé dans d'autres langues. Si un type définit l'ensemble de méthodes d'une interface, il l'implémente. C'est ce qu'on appelle le typage structurel, l'équivalent à la compilation defrappe de canard.

S'il marche comme un canard, nage comme un canard et charlatan comme un canard, c'est un canard

Important: dans Go, l'interface abstraite un ensemble de méthodes (actions), pas de données.

Exemple

L'exemple suivant tiré deComment utiliser les interfaces dans Goillustre une interface de base d'un animal qui peut parler, et différentes structures qui satisfont l'interface:

type Animal interface {
    Speak() string
}

type Dog struct { }

func (d Dog) Speak() string { return “Woof!” }

type Cat struct { }

func (c Cat) Speak() string { return “Meow!” }

type Llama struct { }

func (l Llama) Speak() string { return “???” }

type JavaProgrammer struct { }

func (j JavaProgrammer) Speak() string { return “Design patterns!” }

Vous pouvez construire un [] animal et courirSpeak()sur son contenu:

func main() {
    animals := []Animal{Dog{}, Cat{}, Llama{}, JavaProgrammer{}}
    for _, animal := range animals {
        fmt.Println(animal.Speak())
    }
}

jouer

L'interface vide

LeAnimalL'interface est petite et agréable, mais il y a quelque chose d'encore plus simple, mais un concept un peu complexe à comprendre pour les débutants: l'interface vide.

interface{}est l'interface Go empty, un concept clé. Chaque type l'implémente par définition.

Une interface est un type, vous pouvez donc définir par exemple:

type Dog struct {
    Age interface{}
}

et vous pouvez également passer un type d'interface vide comme paramètre de fonction:

func Eat(t interface{}) {
    // ...
}

Acceptantinterface{}ne signifie pas que la fonction accepte n'importe quel type, mais signifie plutôt que Eat accepte une valeur deinterface{}taper.

Au moment de l'exécution, Go convertira la valeur réelle transmise en uninterface{}évaluer.

Si vous définissez un champ dans une structure de typeinterface{}, vous pouvez lui attribuer une valeur de n'importe quel type. Par exemple:

package main

import “fmt”

type Dog struct { Age interface{} }

func main() { dog := Dog{} dog.Age = “3” fmt.Printf("%#v %T\n", dog.Age, dog.Age)

<span style="color:#a6e22e">dog</span>.<span style="color:#a6e22e">Age</span> = <span style="color:#ae81ff">3</span>
<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">"%#v %T\n"</span>, <span style="color:#a6e22e">dog</span>.<span style="color:#a6e22e">Age</span>, <span style="color:#a6e22e">dog</span>.<span style="color:#a6e22e">Age</span>)

<span style="color:#a6e22e">dog</span>.<span style="color:#a6e22e">Age</span> = <span style="color:#e6db74">"not really an age"</span>
<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">"%#v %T"</span>, <span style="color:#a6e22e">dog</span>.<span style="color:#a6e22e">Age</span>, <span style="color:#a6e22e">dog</span>.<span style="color:#a6e22e">Age</span>)

}

jouer

impressions

"3" string
3 int
"not really an age" string

Bien sûr, cela ne se limite pas aux types de base: vous pouvez y stocker une tranche, une carte, n'importe quelle structure personnalisée.

Comment l'interface est représentée en interne

En interne, une interfaceévaluerc'est deuxwords, ensembles d'octets.

Un mot pointe vers la valeur du type sous-jacent. Un mot pointe vers les données.

Les conversions

Quand nous avons faitanimals := []Animal{Dog{}, Cat{}, Llama{}, JavaProgrammer{}}auparavant, Go convertissait automatiquement tous nos types d'animaux spécifiques en un type d'animal. Chaque élément de cette tranche est maintenant de type Animal, le type sous-jacent pointant vers les espèces spécifiques, comme le chien, le chat et le lama.

Mais si une méthode accepte[]interface{}par exemple, nous ne pouvons pas simplement passeranimalsà lui, car le type ne correspond pas. Nous devons le faireconvertir explicitement chaqueAnimalà uninterface{}avant, avec une boucle, comme décrit dans lePuis-je convertir un [] T en une [] interface {}FAQ:

t := []int{1, 2, 3, 4}
s := make([]interface{}, len(t))
for i, v := range t {
    s[i] = v
}

Déterminez le type sous-jacent d'uninterface{}

Si une valeur est de typeinterface{}, vous souhaiterez peut-être déterminer le type sous-jacent. Comment? Avec un interrupteur sur.(type), aimerGo efficaceexplique:

Un commutateur peut également être utilisé pour découvrir le type dynamique d'une variable d'interface. Un tel commutateur de type utilise la syntaxe d'une assertion de type avec le type de mot-clé entre parenthèses. Si le commutateur déclare une variable dans l'expression, la variable aura le type correspondant dans chaque clause. Il est également idiomatique de réutiliser le nom dans de tels cas, en déclarant en fait une nouvelle variable avec le même nom mais un type différent dans chaque cas.

var t interface{}
t = functionOfSomeType()
switch t := t.(type) {
default:
    fmt.Printf("unexpected type %T\n", t)     // %T prints whatever type t has
case bool:
    fmt.Printf("boolean %t\n", t)             // t has type bool
case int:
    fmt.Printf("integer %d\n", t)             // t has type int
case *bool:
    fmt.Printf("pointer to boolean %t\n", *t) // t has type *bool
case *int:
    fmt.Printf("pointer to integer %d\n", *t) // t has type *int
}

Plus de tutoriels go: