Recherche…


Remarques

Les interfaces dans Go ne sont que des ensembles de méthodes fixes. Un type implémente implicitement une interface si son ensemble de méthodes est un sur-ensemble de l'interface. Il n'y a pas de déclaration d'intention.

Interface simple

Dans Go, une interface n'est qu'un ensemble de méthodes. Nous utilisons une interface pour spécifier un comportement d'un objet donné.

type Painter interface {
    Paint()
}

Le type d'implémentation n'a pas besoin de déclarer qu'il implémente l'interface. Il suffit de définir les méthodes de la même signature.

type Rembrandt struct{}

func (r Rembrandt) Paint() {
    // use a lot of canvas here 
}

Maintenant, nous pouvons utiliser la structure comme interface.

var p Painter
p = Rembrandt{}

Une interface peut être satisfaite (ou implémentée) par un nombre arbitraire de types. De plus, un type peut implémenter un nombre arbitraire d'interfaces.

type Singer interface {
     Sing()
}

type Writer interface {
     Write()
}

type Human struct{}

func (h *Human) Sing() {
    fmt.Println("singing")
}

func (h *Human) Write() {
    fmt.Println("writing")
}



type OnlySinger struct{}
func (o *OnlySinger) Sing() {
    fmt.Println("singing")
}

Ici, Human structure Human satisfait à la fois l'interface Singer et Writer , mais la structure OnlySinger ne satisfait que l'interface Singer .


Interface vide

Il existe un type d'interface vide, qui ne contient aucune méthode. Nous le déclarons comme interface{} . Ceci ne contient aucune méthode, donc chaque type satisfait. L'interface vide peut donc contenir n'importe quelle valeur de type.

var a interface{}
var i int = 5
s := "Hello world"

type StructType struct {
    i, j int
    k string
}


// all are valid statements
a = i
a = s
a = &StructType{1, 2, "hello"}

Le cas d'utilisation le plus courant des interfaces est de s'assurer qu'une variable prend en charge un ou plusieurs comportements. En revanche, le principal cas d'utilisation de l'interface vide est de définir une variable pouvant contenir n'importe quelle valeur, quel que soit son type concret.

Pour récupérer ces valeurs comme leurs types originaux, il suffit de faire

i = a.(int)
s = a.(string)
m := a.(*StructType)

ou

i, ok := a.(int)
s, ok := a.(string)
m, ok := a.(*StructType)

ok indique si l' interface a est convertible en type donné. S'il est impossible de jeter ok sera false .


Valeurs d'interface

Si vous déclarez une variable d'une interface, il peut stocker n'importe quel type de valeur qui implémente les méthodes déclarées par l'interface!

Si nous déclarons h de l' interface Singer , il peut stocker une valeur de type Human ou OnlySinger. Cela est dû au fait qu'ils implémentent tous des méthodes spécifiées par l'interface Singer .

var h Singer
h = &human{}

h.Sing()

Détermination du type sous-jacent de l'interface

Au départ, il peut parfois être utile de savoir quel type sous-jacent vous avez été transmis. Cela peut être fait avec un commutateur de type. Cela suppose que nous ayons deux structures:

type Rembrandt struct{}

func (r Rembrandt) Paint() {}

type Picasso struct{}

func (r Picasso) Paint() {}

Cela implémente l'interface Painter:

type Painter interface {
    Paint()
}

Ensuite, nous pouvons utiliser ce commutateur pour déterminer le type sous-jacent:

func WhichPainter(painter Painter) {
    switch painter.(type) {
    case Rembrandt:
        fmt.Println("The underlying type is Rembrandt")
    case Picasso:
        fmt.Println("The underlying type is Picasso")
    default:
        fmt.Println("Unknown type")
    }
}

Vérification au moment de la compilation si un type satisfait à une interface

Les interfaces et les implémentations (types implémentant une interface) sont "détachées". Il est donc légitime de vérifier à la compilation si un type implémente une interface.

Une façon de demander au compilateur de vérifier que le type T implémente l'interface I est en essayant une affectation en utilisant la valeur zéro pour T ou pointeur sur T , selon le cas. Et nous pouvons choisir d'affecter à l' identificateur vide pour éviter les déchets inutiles:

type T struct{}

var _ I = T{}       // Verify that T implements I.
var _ I = (*T)(nil) // Verify that *T implements I.

Si T ou *T n'implémente pas I , ce sera une erreur de compilation.

Cette question apparaît également dans la FAQ officielle: Comment puis-je garantir que mon type satisfait une interface?

Commutateur de type

Les commutateurs de type peuvent également être utilisés pour obtenir une variable correspondant au type de la casse:

func convint(v interface{}) (int,error) {
    switch u := v.(type) {
    case int:
        return u, nil
    case float64:
        return int(u), nil
    case string:
        return strconv(u)
    default:
        return 0, errors.New("Unsupported type")
    }
}

Type Assertion

Vous pouvez accéder au type de données réel de l'interface avec Type Assertion.

interfaceVariable.(DataType)

Exemple de struct MyType qui implémente l'interface Subber :

package main

import (
    "fmt"
)

type Subber interface {
    Sub(a, b int) int
}

type MyType struct {
    Msg string
}
 
//Implement method Sub(a,b int) int
func (m *MyType) Sub(a, b int) int {
    m.Msg = "SUB!!!"

    return a - b;
}

func main() {
    var interfaceVar Subber = &MyType{}
    fmt.Println(interfaceVar.Sub(6,5))
    fmt.Println(interfaceVar.(*MyType).Msg)
}

Sans .(*MyType) nous ne pourrions pas accéder à Msg Field. Si nous essayons interfaceVar.Msg il affichera une erreur de compilation:

interfaceVar.Msg undefined (type Subber has no field or method Msg)

Aller des interfaces d'un aspect mathématique

En mathématiques, en particulier la théorie des ensembles , nous avons une collection de choses appelée ensemble et nous les appelons éléments . Nous montrons un ensemble avec son nom comme A, B, C, ... ou explicitement en mettant son membre sur la notation entre accolades: {a, b, c, d, e}. Supposons que nous ayons un élément arbitraire x et un ensemble Z. La question clé est: "Comment pouvons-nous comprendre que x est membre de Z ou non?". Mathématicien répond à cette question avec un concept: Propriété caractéristique d'un ensemble. Caractéristique La propriété d'un ensemble est une expression qui décrit l'ensemble. Par exemple, nous avons un ensemble de nombres naturels qui est {0, 1, 2, 3, 4, 5, ...}. Nous pouvons décrire cet ensemble avec cette expression: {a n | a 0 = 0, a n = a n-1 +1}. Dans la dernière expression a 0 = 0, a n = a n-1 +1 est la propriété caractéristique de l'ensemble des nombres naturels. Si nous avons cette expression, nous pouvons construire cet ensemble complètement . Soit décrire l'ensemble des nombres pairs de cette manière. Nous savons que cet ensemble est composé des nombres suivants: {0, 2, 4, 6, 8, 10, ...}. En un coup d'œil, nous comprenons que tous ces nombres sont aussi un nombre naturel , en d'autres termes, si nous ajoutons des conditions supplémentaires à la propriété caractéristique des nombres naturels, nous pouvons construire une nouvelle expression décrivant cet ensemble . On peut donc décrire avec cette expression: {n | n est un membre de nombres naturels et le rappel de n sur 2 est zéro}. Maintenant, nous pouvons créer un filtre qui obtient la propriété caractéristique d'un ensemble et filtrer certains éléments souhaités pour renvoyer des éléments de notre ensemble. Par exemple, si nous avons un filtre de nombres naturels, à la fois les nombres naturels et les nombres pairs peuvent passer ce filtre, mais si nous avons un filtre de nombres pairs, certains éléments comme 3 et 137871 ne peuvent pas passer le filtre.

La définition de l'interface dans Go est comme définir la propriété caractéristique et le mécanisme d'utilisation de l'interface car un argument d'une fonction est comme un filtre qui détecte que l'élément est un membre de notre ensemble souhaité ou non. Décrivons cet aspect avec le code:

type Number interface {
    IsNumber() bool // the implementation filter "meysam" from 3.14, 2 and 3
}

type NaturalNumber interface {
    Number
    IsNaturalNumber() bool // the implementation filter 3.14 from 2 and 3
}

type EvenNumber interface {
    NaturalNumber
    IsEvenNumber() bool // the implementation filter 3 from 2
}

La propriété caractéristique de Number est constituée de toutes les structures qui ont la méthode IsNumber . Pour NaturalNumber toutes les méthodes IsNaturalNumber méthodes IsNumber et IsNaturalNumber et enfin, pour EvenNumber toutes les méthodes IsEvenNumber méthodes IsNumber , IsNaturalNumber et IsEvenNumber . Grâce à cette interprétation de l'interface, nous pouvons facilement comprendre que l' interface{} ne possédant aucune propriété caractéristique, acceptez tous les types (car elle ne possède pas de filtre pour distinguer les valeurs).



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow