Was bedeutet Schnittstelle {}?


133

Ich bin neu in Schnittstellen und versuche, SOAP-Anfragen von Github zu machen

Ich verstehe die Bedeutung von nicht

Msg interface{}

in diesem Code:

type Envelope struct {
    Body `xml:"soap:"`
}

type Body struct {
    Msg interface{}
}

Ich habe die gleiche Syntax in beobachtet

fmt.Println

aber verstehe nicht, was durch erreicht wird

interface{}

20
interface{}ist mehr oder weniger das Äquivalent von void *in C. Es kann auf alles verweisen und Sie benötigen eine Cast / Type-Zusicherung, um es zu verwenden.
Nick Craig-Wood

Was bedeutet Schnittstelle {}? Siehe stackoverflow.com/a/62337836/12817546 .
Tom J

Antworten:


189

Sie können auf den Artikel " Verwendung von Schnittstellen in Go " verweisen (basierend auf " Russ Cox 'Beschreibung der Schnittstellen "):

Was ist eine Schnittstelle?

Eine Schnittstelle besteht aus zwei Dingen:

  • es ist eine Reihe von Methoden,
  • es ist aber auch ein typ

Der interface{}Typ, die leere Schnittstelle, ist die Schnittstelle, die keine Methoden hat.

Da es kein Schlüsselwort implementiert gibt, implementieren alle Typen mindestens null Methoden, und die Erfüllung einer Schnittstelle erfolgt automatisch. Alle Typen erfüllen die leere Schnittstelle .
Das heißt, wenn Sie eine Funktion schreiben, die einen interface{}Wert als Parameter verwendet, können Sie diese Funktion mit einem beliebigen Wert versehen .

(Das ist, was Msgin Ihrer Frage darstellt: jeder Wert)

func DoSomething(v interface{}) {
   // ...
}

Hier wird es verwirrend:

Was ist der Typ innerhalb der DoSomethingFunktion ?v

Anfänger glauben, dass „ vvon jeder Art ist“, aber das ist falsch.
vist von keiner Art; es ist vom interface{}Typ .

Wenn Sie einen Wert an die DoSomethingFunktion übergeben, führt die Go-Laufzeit (falls erforderlich) eine Typkonvertierung durch und konvertiert den Wert in einen interface{}Wert .
Alle Werte haben zur Laufzeit genau einen Typ und vder eine statische Typ interface{}.

Ein Schnittstellenwert besteht aus zwei Datenwörtern :

  • Ein Wort wird verwendet, um auf eine Methodentabelle für den zugrunde liegenden Typ des Werts zu verweisen.
  • und das andere Wort wird verwendet, um auf die tatsächlichen Daten zu verweisen, die von diesem Wert gehalten werden.

Nachtrag: Hier ist Russ 'Artikel bezüglich einer Schnittstellenstruktur ziemlich vollständig:

type Stringer interface {
    String() string
}

Schnittstellenwerte werden als Zwei-Wort-Paar dargestellt, das einen Zeiger auf Informationen über den in der Schnittstelle gespeicherten Typ und einen Zeiger auf die zugehörigen Daten gibt.
Durch Zuweisen von b zu einem Schnittstellenwert vom Typ Stringer werden beide Wörter des Schnittstellenwerts festgelegt.

http://research.swtch.com/gointer2.png

Das erste Wort im Schnittstellenwert zeigt auf das, was ich als Schnittstellentabelle oder itable bezeichne (ausgesprochen i-table; in den Laufzeitquellen lautet der Name der C-Implementierung Itab).
Die itable beginnt mit einigen Metadaten zu den beteiligten Typen und wird dann zu einer Liste von Funktionszeigern.
Beachten Sie, dass die itable dem Schnittstellentyp entspricht, nicht dem dynamischen Typ .
In unserem Beispiel Stringerlistet die itable für das Halten von Typ Binary die Methoden auf, die verwendet werden, um Stringer zu erfüllen. Dies ist nur String: Die anderen Methoden ( Get) von Binary erscheinen nicht in der itable.

Das zweite Wort im Schnittstellenwert zeigt auf die tatsächlichen Daten , in diesem Fall eine Kopie von b.
Die Zuweisung erstellt var s Stringer = beine Kopie von bund zeigt nicht auf bdenselben Grund, var c uint64 = baus dem eine Kopie erstellt wird: Wenn sich bspäter etwas ändert sund cder ursprüngliche Wert haben soll, nicht der neue.
In Schnittstellen gespeicherte Werte können beliebig groß sein, aber nur ein Wort ist für das Halten des Werts in der Schnittstellenstruktur vorgesehen, sodass die Zuweisung einen Speicherblock auf dem Heap zuweist und den Zeiger im Ein-Wort-Slot aufzeichnet.


4
Was meinst du mit "zwei Datenwörtern"? Was bedeutet das "Wort" konkret?
Mingyu

3
@Mingyu Ich habe die Antwort vervollständigt, um diese beiden Wörter (32-Bit-Punkte) zu veranschaulichen.
VonC

2
@Mingyu: VonC bezieht sich auf ein Wort im Sinne der Computerarchitektur - eine Sammlung von Bits, die ein Datenelement fester Größe definieren. Die Wortgröße wird von der von Ihnen verwendeten Prozessorarchitektur bestimmt.
Dan Esparza

1
danke @VonC für deine Antwort ... die Wahrheit ist, dass ich es leid bin, einen Downpost zu bekommen, wenn ich Dinge frage. Die meisten Leute sagen mir, dass ich die Dokumente lesen soll ... Ich werde mich an deinen Vorschlag erinnern, wenn ich mit fühle Ich werde einen Beitrag dafür richtig schreiben ... aber ich kann mir wirklich keinen anderen Weg überlegen, um zu fragen. Also trotzdem danke und entschuldige meinen niedrigen Willen. Sie können sich dies gerne ansehen: stackoverflow.com/questions/45577301/… um zu klären, warum ich nicht gerne frage.
Victor

1
@vic kein Problem, und entschuldigen Sie Ihre früheren schlechten Erfahrungen als Fragesteller. Es ist nur so, dass Kommentare schlecht zu Fragen und Antworten passen.
VonC

34

interface{}bedeutet, dass Sie einen beliebigen Wert eingeben können, einschließlich Ihres eigenen benutzerdefinierten Typs. Alle Typen in Go erfüllen eine leere Schnittstelle ( interface{}ist eine leere Schnittstelle).
In Ihrem Beispiel kann das Msg-Feld einen beliebigen Wert haben.

Beispiel:

package main

import (
    "fmt"
)

type Body struct {
    Msg interface{}
}

func main() {
    b := Body{}
    b.Msg = "5"
    fmt.Printf("%#v %T \n", b.Msg, b.Msg) // Output: "5" string
    b.Msg = 5

    fmt.Printf("%#v %T", b.Msg, b.Msg) //Output:  5 int
}

Geh zum Spielplatz


12

Es wird als leere Schnittstelle bezeichnet und von allen Typen implementiert, was bedeutet, dass Sie alles in das MsgFeld einfügen können .

Beispiel:

body := Body{3}
fmt.Printf("%#v\n", body) // -> main.Body{Msg:3}

body = Body{"anything"}
fmt.Printf("%#v\n", body) // -> main.Body{Msg:"anything"}

body = Body{body}
fmt.Printf("%#v\n", body) // -> main.Body{Msg:main.Body{Msg:"anything"}}

Dies ist die logische Erweiterung der Tatsache, dass ein Typ eine Schnittstelle implementiert, sobald er über alle Methoden der Schnittstelle verfügt.


bedeutet, es könnte eine int zu benutzerdefinierte Struktur sein?
Benutzer

11

Hier gibt es bereits gute Antworten. Lassen Sie mich auch meine eigenen für andere hinzufügen, die es intuitiv verstehen wollen:


Schnittstelle

Hier ist eine Schnittstelle mit einer Methode:

type Runner interface {
    Run()
}

Jeder Typ mit einer Run()Methode erfüllt also die Runner-Schnittstelle:

type Program struct {
    /* fields */
}

func (p Program) Run() {
    /* running */
}

func (p Program) Stop() {
    /* stopping */
}
  • Obwohl der Programmtyp auch über eine Stop-Methode verfügt, erfüllt er die Runner-Schnittstelle weiterhin, da lediglich alle Methoden einer Schnittstelle erforderlich sind, um diese zu erfüllen.

  • Es hat also eine Run-Methode und erfüllt die Runner-Schnittstelle.


Leere Schnittstelle

Hier ist eine benannte leere Schnittstelle ohne Methoden:

type Empty interface {
    /* it has no methods */
}

Jeder Typ erfüllt also diese Schnittstelle. Da keine Methode erforderlich ist, um diese Schnittstelle zu erfüllen. Beispielsweise:

// Because, Empty interface has no methods, following types satisfy the Empty interface
var a Empty

a = 5
a = 6.5
a = "hello"

Aber erfüllt der oben genannte Programmtyp dies? Ja:

a = Program{} // ok

Schnittstelle {} entspricht der obigen leeren Schnittstelle.

var b interface{}

// true: a == b

b = a
b = 9
b = "bye"

Wie Sie sehen, ist daran nichts Geheimnisvolles, aber es ist sehr leicht zu missbrauchen. Halte dich so weit wie möglich davon fern.


https://play.golang.org/p/A-vwTddWJ7G


Das type Runner interfacewird im Go-Spielplatzbeispiel nicht verwendet.
Tom J

9

Aus den Golang-Spezifikationen :

Ein Schnittstellentyp gibt einen Methodensatz an, der als Schnittstelle bezeichnet wird. Eine Variable vom Schnittstellentyp kann einen Wert eines beliebigen Typs mit einem Methodensatz speichern, der eine beliebige Obermenge der Schnittstelle ist. Ein solcher Typ soll die Schnittstelle implementieren. Der Wert einer nicht initialisierten Variablen vom Schnittstellentyp ist Null.

Ein Typ implementiert eine beliebige Schnittstelle, die eine beliebige Teilmenge seiner Methoden umfasst, und kann daher mehrere unterschiedliche Schnittstellen implementieren. Beispielsweise implementieren alle Typen die leere Schnittstelle:

Schnittstelle{}

Die Konzepte für Graps sind:

  1. Alles hat einen Typ . Sie können einen neuen Typ definieren, nennen wir es T. Sagen wir nun unsere Art That drei Methoden: A, B, C.
  2. Die für einen Typ angegebenen Methoden werden als " Schnittstellentyp " bezeichnet. Nennen wir es in unserem Beispiel: T_interface. EntsprichtT_interface = (A, B, C)
  3. Sie können einen "Schnittstellentyp" erstellen, indem Sie die Signatur der Methoden definieren.MyInterface = (A, )
  4. Wenn Sie eine Variable vom Typ "Schnittstellentyp" angeben , können Sie ihr nur Typen zuweisen, deren Schnittstelle eine Obermenge Ihrer Schnittstelle ist. Das bedeutet, dass alle darin enthaltenen Methoden enthalten sein MyInterfacemüssenT_interface

Sie können daraus schließen, dass alle "Schnittstellentypen" aller Typen eine Obermenge der leeren Schnittstelle sind.


1

Ein Beispiel, das die hervorragende Antwort von @VonC und den Kommentar von @ NickCraig-Wood erweitert. interface{}kann auf alles verweisen und Sie benötigen eine Cast / Type-Zusicherung, um es zu verwenden.

package main

import (
    . "fmt"
    "strconv"
)

var c = cat("Fish")
var d = dog("Bone")

func main() {
    var i interface{} = c
    switch i.(type) {
    case cat:
        c.Eat() // Fish
    }

    i = d
    switch i.(type) {
    case dog:
        d.Eat() // Bone
    }

    i = "4.3"
    Printf("%T %v\n", i, i) // string 4.3
    s, _ := i.(string)      // type assertion
    f, _ := strconv.ParseFloat(s, 64)
    n := int(f)             // type conversion
    Printf("%T %v\n", n, n) // int 4
}

type cat string
type dog string
func (c cat) Eat() { Println(c) }
func (d dog) Eat() { Println(d) }

iist eine Variable einer leeren Schnittstelle mit einem Wert cat("Fish"). Es ist zulässig, einen Methodenwert aus einem Wert vom Schnittstellentyp zu erstellen. Siehe https://golang.org/ref/spec#Interface_types .

Ein Typschalter bestätigt, dass der iSchnittstellentyp ist cat("Fish"). Siehe https://golang.org/doc/effective_go.html#type_switch . iwird dann neu zugewiesen dog("Bone"). Ein Typschalter bestätigt, dass sich der iSchnittstellentyp in geändert hat dog("Bone").

Sie können den Compiler auch bitten, zu überprüfen, ob der Typ Tdie Schnittstelle implementiert, Iindem Sie versuchen, eine Zuweisung vorzunehmen : var _ I = T{}. Siehe https://golang.org/doc/faq#guarantee_satisfies_interface und https://stackoverflow.com/a/60663003/12817546 .

Alle Typen implementieren die leere Schnittstelle interface{}. Siehe https://talks.golang.org/2012/goforc.slide#44 und https://golang.org/ref/spec#Interface_types . In diesem Beispiel iwird diesmal eine Zeichenfolge "4.3" neu zugewiesen. iwird dann zu einem neuen String - Variable zugeordnet smit i.(string)bevor szu einem float64 Typ umgewandelt wird fverwendet strconv. Schließlich fwird in neinen int-Typ gleich 4 konvertiert. Siehe Was ist der Unterschied zwischen Typkonvertierung und Typzusicherung?

Dank der integrierten Karten und Slices von Go und der Möglichkeit, die leere Schnittstelle zum Erstellen von Containern (mit explizitem Unboxing) zu verwenden, ist es in vielen Fällen möglich, Code zu schreiben, der das tut, was Generika ermöglichen würden, wenn auch weniger reibungslos. Siehe https://golang.org/doc/faq#generics .


Code mit einer Schnittstelle entkoppeln. Siehe stackoverflow.com/a/62297796/12817546 . Rufen Sie eine Methode "dynamisch" auf. Siehe stackoverflow.com/a/62336440/12817546 . Greifen Sie auf ein Go-Paket zu. Siehe stackoverflow.com/a/62278078/12817546 . Weisen Sie einer Variablen einen beliebigen Wert zu. Siehe stackoverflow.com/a/62337836/12817546 .
Tom J
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.