Suche…


Bemerkungen

Schnittstellen in Go sind nur feste Methodensätze. Ein Typ implementiert implizit eine Schnittstelle, wenn deren Methodensatz eine Obermenge der Schnittstelle ist. Es gibt keine Absichtserklärung.

Einfache Schnittstelle

In Go ist eine Schnittstelle nur eine Reihe von Methoden. Wir verwenden eine Schnittstelle, um ein Verhalten eines bestimmten Objekts anzugeben.

type Painter interface {
    Paint()
}

Der implementierende Typ muss nicht angeben, dass er die Schnittstelle implementiert. Es reicht aus, Methoden mit derselben Signatur zu definieren.

type Rembrandt struct{}

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

Jetzt können wir die Struktur als Schnittstelle verwenden.

var p Painter
p = Rembrandt{}

Eine Schnittstelle kann von einer beliebigen Anzahl von Typen erfüllt (oder implementiert) werden. Ein Typ kann auch eine beliebige Anzahl von Schnittstellen implementieren.

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

Die Human Struktur OnlySinger sowohl der Singer als auch der Writer Schnittstelle, die OnlySinger Struktur jedoch nur der Singer Schnittstelle.


Leere Schnittstelle

Es gibt einen leeren Schnittstellentyp, der keine Methoden enthält. Wir erklären es als interface{} . Das enthält keine Methoden, so dass jeder type erfüllt. Daher kann die leere Schnittstelle einen beliebigen Typwert enthalten.

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

Der häufigste Anwendungsfall für Schnittstellen besteht darin, sicherzustellen, dass eine Variable ein oder mehrere Verhalten unterstützt. Im Gegensatz dazu besteht der Hauptanwendungsfall für die leere Schnittstelle darin, eine Variable zu definieren, die unabhängig von ihrem konkreten Typ einen beliebigen Wert enthalten kann.

Um diese Werte wieder als ihre ursprünglichen Typen zu erhalten, müssen wir nur noch tun

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

oder

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

ok zeigt an, ob die interface a in einen gegebenen Typ konvertierbar ist. Wenn dies nicht möglich ist, ist ok false .


Schnittstellenwerte

Wenn Sie eine Variable einer Schnittstelle deklarieren, kann sie jeden Werttyp speichern, der die von der Schnittstelle deklarierten Methoden implementiert.

Wenn wir h der interface Singer deklarieren, kann ein Wert vom Typ Human oder OnlySinger. Dies liegt daran, dass sie alle Methoden implementieren, die von der Singer Schnittstelle angegeben werden.

var h Singer
h = &human{}

h.Sing()

Festlegen des zugrunde liegenden Typs über die Schnittstelle

Unterwegs kann es manchmal nützlich sein, zu wissen, welcher Basistyp Sie übergeben haben. Dies kann mit einem Typwechsel erfolgen. Dies setzt voraus, dass wir zwei Strukturen haben:

type Rembrandt struct{}

func (r Rembrandt) Paint() {}

type Picasso struct{}

func (r Picasso) Paint() {}

Implementieren Sie die Painter-Schnittstelle:

type Painter interface {
    Paint()
}

Dann können wir diesen Schalter verwenden, um den zugrunde liegenden Typ zu bestimmen:

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

Überprüfung der Kompilierzeit, ob ein Typ eine Schnittstelle erfüllt

Schnittstellen und Implementierungen (Typen, die eine Schnittstelle implementieren) werden "getrennt". Es ist also eine berechtigte Frage, wie man zur Kompilierzeit prüfen kann, ob ein Typ eine Schnittstelle implementiert.

Eine Möglichkeit, den Compiler zu fragen, ob der Typ T die Schnittstelle I implementiert, besteht darin, eine Zuweisung unter Verwendung des Nullwerts für T oder des Zeigers auf T versuchen. Und wir können dem leeren Bezeichner zuweisen, um unnötigen Müll zu vermeiden:

type T struct{}

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

Wenn T oder *T kein I implementieren, liegt ein Fehler bei der Kompilierung vor.

Diese Frage erscheint auch in der offiziellen FAQ: Wie kann ich garantieren, dass mein Typ eine Schnittstelle erfüllt?

Typ wechseln

Typenschalter können auch verwendet werden, um eine Variable abzurufen, die dem Typ des Falls entspricht:

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

Typ Assertion

Sie können mit Type Assertion auf den realen Datentyp der Schnittstelle zugreifen.

interfaceVariable.(DataType)

Beispiel für struct MyType das die Schnittstelle Subber implementiert:

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

Ohne .(*MyType) wir keinen Zugriff auf Msg Field. Wenn wir interfaceVar.Msg versuchen, wird ein Kompilierungsfehler angezeigt:

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

Gehen Sie auf Schnittstellen aus einem mathematischen Aspekt

In der Mathematik, insbesondere der Satztheorie , haben wir eine Sammlung von Dingen, die als Set bezeichnet wird, und wir nennen diese Dinge als Elemente . Wir zeigen ein Set mit seinem Namen wie A, B, C, ... oder explizit, indem das Element in eine geschweifte Klammer geschrieben wird: {a, b, c, d, e}. Angenommen, wir haben ein beliebiges Element x und eine Menge Z. Die Schlüsselfrage lautet: "Wie können wir verstehen, dass x ein Mitglied von Z ist oder nicht?". Die Antwort auf diese Frage gibt der Mathematiker mit einem Konzept: Charakteristikum eines Satzes. Charakteristische Eigenschaft einer Menge ist ein Ausdruck, der eine Menge vollständig beschreibt. Zum Beispiel haben wir natürliche Zahlen gesetzt, die {0, 1, 2, 3, 4, 5, ...} sind. Wir können diese Menge mit diesem Ausdruck beschreiben: {a n | a 0 = 0 = a n-1 a n + 1}. In letzten Ausdruck a 0 = 0 = a n-1 a n + 1 die charakteristische Eigenschaft der Menge der natürlichen Zahlen ist. Wenn wir diesen Ausdruck haben, können wir diesen Satz vollständig erstellen . Beschreiben Sie die Anzahl der geraden Zahlen auf diese Weise. Wir wissen, dass diese Menge aus folgenden Zahlen besteht: {0, 2, 4, 6, 8, 10, ...}. Mit einem Blick verstehen wir, dass alle diese Zahlen auch eine natürliche Zahl sind. Mit anderen Worten, wenn wir der charakteristischen Eigenschaft der natürlichen Zahlen zusätzliche Bedingungen hinzufügen, können wir einen neuen Ausdruck erstellen, der diese Menge beschreibt . Wir können also mit diesem Ausdruck beschreiben: {n | n ist ein Mitglied von natürlichen Zahlen und die Erinnerung an n auf 2 ist null}. Jetzt können wir einen Filter erstellen, der die charakteristische Eigenschaft eines Sets erhält, und einige gewünschte Elemente filtern, um Elemente unseres Sets zurückzugeben. Wenn wir beispielsweise einen Filter für natürliche Zahlen haben, können sowohl natürliche als auch gerade Zahlen diesen Filter passieren, aber wenn wir einen Filter für gerade Zahlen haben, können einige Elemente wie 3 und 137871 den Filter nicht passieren.

Die Definition der Schnittstelle in Go ist wie das Definieren der charakteristischen Eigenschaft und der Mechanismus der Verwendung der Schnittstelle als Argument einer Funktion ist wie ein Filter, der erkennt, dass das Element ein Mitglied unserer gewünschten Menge ist oder nicht. Beschreiben wir diesen Aspekt mit 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
}

Die charakteristische Eigenschaft von Number ist alle Strukturen, die die IsNumber Methode haben, für NaturalNumber sind alle diejenigen, die die IsNumber und IsNaturalNumber Methode haben, und schließlich für EvenNumber sind alle Typen, die die IsNumber , IsNaturalNumber und IsEvenNumber Methode haben. Dank dieser Interpretation des Interfaces können wir leicht verstehen, dass das interface{} keine charakteristische Eigenschaft besitzt und alle Typen akzeptiert (da es keinen Filter zur Unterscheidung von Werten gibt).



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow