Go
interfaces
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).