Zoeken…


Opmerkingen

Interfaces in Go zijn gewoon vaste methodesets. Een type implementeert impliciet een interface als de ingestelde methode een superset van de interface is. Er is geen intentieverklaring.

Eenvoudige interface

In Go is een interface slechts een set methoden. We gebruiken een interface om een gedrag van een bepaald object te specificeren.

type Painter interface {
    Paint()
}

Het implementatietype hoeft niet te verklaren dat het de interface implementeert. Het is voldoende om methoden met dezelfde handtekening te definiëren.

type Rembrandt struct{}

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

Nu kunnen we de structuur als interface gebruiken.

var p Painter
p = Rembrandt{}

Aan een interface kan worden voldaan (of geïmplementeerd) door een willekeurig aantal typen. Ook kan een type een willekeurig aantal interfaces implementeren.

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

Hier, The Human struct voldoen aan zowel de Singer en Writer interface, maar de OnlySinger struct alleen aan Singer interface.


Lege interface

Er is een leeg interfacetype dat geen methoden bevat. We verklaren het als interface{} . Dit bevat geen methoden, dus elk type voldoet eraan. Daarom kan een lege interface elke typewaarde bevatten.

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

De meest voorkomende use case voor interfaces is ervoor te zorgen dat een variabele een of meer gedragingen ondersteunt. Het primaire gebruik van de lege interface is daarentegen het definiëren van een variabele die elke waarde kan bevatten, ongeacht het concrete type.

Om deze waarden terug te krijgen als hun oorspronkelijke typen, hoeven we alleen maar te doen

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

of

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

ok geeft aan of de interface a converteerbaar is naar het gegeven type. Als het niet mogelijk is om ok te casten, is dit false .


Interface waarden

Als u een variabele van een interface declareert, kan deze elk waardetype opslaan dat de methoden implementeert die door de interface zijn aangegeven!

Als we h van interface Singer verklaren, kan deze een waarde van het type Human of OnlySinger. Dit komt omdat ze allemaal methoden implementeren die zijn gespecificeerd door de Singer interface.

var h Singer
h = &human{}

h.Sing()

Bepaling van het onderliggende type van de interface

Het kan soms handig zijn om te weten welk onderliggend type je hebt doorgegeven. Dit kan met een typeschakelaar. Dit veronderstelt dat we twee structuren hebben:

type Rembrandt struct{}

func (r Rembrandt) Paint() {}

type Picasso struct{}

func (r Picasso) Paint() {}

Die de Painter-interface implementeren:

type Painter interface {
    Paint()
}

Vervolgens kunnen we deze schakelaar gebruiken om het onderliggende type te bepalen:

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

Compile-time check of een type voldoet aan een interface

Interfaces en implementaties (types die een interface implementeren) zijn "vrijstaand". Het is dus een terechte vraag hoe u tijdens het compileren kunt controleren of een type een interface implementeert.

Een manier om de compiler te vragen om te controleren of het type T de interface I implementeert, is door een toewijzing te proberen met de nulwaarde voor T of aanwijzer naar T , al naar gelang. En we kunnen ervoor kiezen om de blanco ID toe te wijzen om onnodig afval te voorkomen:

type T struct{}

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

Als T of *T I niet implementeert, is er een compilatietijdfout.

Deze vraag verschijnt ook in de officiële FAQ: Hoe kan ik garanderen dat mijn type voldoet aan een interface?

Type schakelaar

Type-switches kunnen ook worden gebruikt om een variabele te krijgen die overeenkomt met het type case:

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 bewering

U kunt toegang krijgen tot het echte gegevenstype van de interface met Type Assertion.

interfaceVariable.(DataType)

Voorbeeld van struct MyType die interface Subber implementeren:

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

Zonder .(*MyType) zouden we geen toegang kunnen krijgen tot Msg Field. Als we interfaceVar.Msg proberen, zal dit een compileerfout tonen:

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

Ga naar interfaces vanuit een wiskundig aspect

In de wiskunde, met name Set Theory , hebben we een verzameling dingen die set wordt genoemd en we noemen die dingen als elementen . We tonen een set met de naam A, B, C, ... of expliciet met het plaatsen van het lid op accolade: {a, b, c, d, e}. Stel dat we een willekeurig element x en een set Z hebben. De kernvraag is: "Hoe kunnen we begrijpen dat x lid is van Z of niet?". Wiskundige antwoord op deze vraag met een concept: karakteristieke eigenschap van een set. Karakteristieke eigenschap van een set is een uitdrukking die de set volledig beschrijft. We hebben bijvoorbeeld een reeks natuurlijke getallen die {0, 1, 2, 3, 4, 5, ...} is. We kunnen deze set beschrijven met deze expressie: {a n | a 0 = 0, a n = a n-1 +1}. In de laatste uitdrukking is a 0 = 0, a n = a n-1 +1 de karakteristieke eigenschap van een verzameling natuurlijke getallen. Als we deze uitdrukking hebben, kunnen we deze set volledig bouwen . Laten we de reeks even getallen op deze manier beschrijven. We weten dat deze set bestaat uit deze getallen: {0, 2, 4, 6, 8, 10, ...}. In één oogopslag begrijpen we dat al deze getallen ook een natuurlijk getal zijn , met andere woorden, als we wat extra voorwaarden toevoegen aan de kenmerkende eigenschap van natuurlijke getallen, kunnen we een nieuwe uitdrukking bouwen die deze set beschrijft . Dus we kunnen beschrijven met deze uitdrukking: {n | n is een lid van natuurlijke getallen en de herinnering aan n op 2 is nul}. Nu kunnen we een filter maken dat de karakteristieke eigenschap van een set krijgt en een aantal gewenste elementen filteren om elementen van onze set te retourneren. Als we bijvoorbeeld een natuurlijk getallenfilter hebben, kunnen zowel natuurlijke getallen als even getallen dit filter doorgeven, maar als we een even getallenfilter hebben, kunnen sommige elementen zoals 3 en 137871 niet door het filter.

De definitie van interface in Go is als het definiëren van de karakteristieke eigenschap en het mechanisme van het gebruik van interface als een argument van een functie is als een filter dat detecteert dat het element een lid is van onze gewenste set of niet. Laten we dit aspect beschrijven met 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
}

De karakteristieke eigenschap van Number is alle structuren die moeten IsNumber methode, want NaturalNumber is al degenen die hebben IsNumber en IsNaturalNumber methoden en tenslotte voor EvenNumber is alle soorten waarvan IsNumber , IsNaturalNumber en IsEvenNumber methoden. Dankzij deze interpretatie van de interface kunnen we gemakkelijk begrijpen dat, aangezien interface{} geen karakteristieke eigenschap heeft, alle typen worden geaccepteerd (omdat het geen filter heeft om onderscheid te maken tussen waarden).



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow