Ein Interface-Typ ist in seiner einfachsten Form eine Menge von Methodensignaturen.
Hier ist ein Beispiel für eine Interface-Definition, die zwei Methoden Add und Value enthält:
type Counter interface {
Add(increment int)
Value() int
}
Die Parameternamen wie increment können in der Interface-Definition weggelassen werden, aber sie erhöhen oft die Lesbarkeit.
Interface-Namen in Go enthalten nicht das Wort Interface oder I.
Stattdessen enden sie oft mit er, z. B. Reader, Stringer.
Jeder Typ, der die Methoden des Interfaces definiert, „implementiert“ das Interface automatisch und implizit.
In Go gibt es kein implements-Schlüsselwort.
Der folgende Typ implementiert das Interface Counter, das wir oben gesehen haben.
type Stats struct {
value int
// ...
}
func (s Stats) Add(v int) {
s.value += v
}
func (s Stats) Value() int {
return s.value
}
func (s Stats) SomeOtherMethod() {
// The type can have additional methods not mentioned in the interface.
}
Für die Implementierung des Interfaces spielt es keine Rolle, ob die Methode einen Wert- oder Zeigerempfänger hat. (Schau dir die Methoden-Konzepte noch einmal an, wenn du dir bei diesen unsicher bist.)
Ein Wert vom Interface-Typ kann jeden Wert aufnehmen, der diese Methoden implementiert. 1
Das bedeutet, dass Stats jetzt überall verwendet werden kann, wo das Interface Counter erwartet wird.
func SetUpAnalytics(counter Counter) {
// ...
}
stats := Stats{}
SetUpAnalytics(stats)
// works because Stats implements Counter
Wenn alle Funktionsparameter und Rückgabewerte konkrete Typen (also Nicht-Interface-Typen) verwenden müssten, würde das ihre Wiederverwendbarkeit stark einschränken. Durch die Verwendung von Interface-Typen für die Parameter wird nur das Verhalten definiert, das die Funktion benötigt, um ihre Aufgabe zu erfüllen. Jetzt kann die Funktion mit Parametern unterschiedlicher konkreter Typen verwendet werden, solange diese konkreten Typen das Interface implementieren. Dieselbe Logik gilt, wenn Interface-Typen als Rückgabewerte verwendet werden.
Weil Interfaces implizit implementiert werden, kann ein Typ leicht mehrere Interfaces implementieren. Er muss nur alle notwendigen Methoden definiert haben.
Interface-Definitionen können andere Interfaces enthalten. Hier ist ein Beispiel aus der Standardbibliothek.
type ReadCloser interface {
Reader
Closer
}
// Based on these interfaces:
type Reader interface {
Read(p []byte) (n int, err error)
}
type Closer interface {
Close() error
}
Anstatt die Methodensignaturen für Read und Close zu wiederholen, nutzt die Interface-Definition die vorhandenen Definitionen von Reader und Closer, um ReadCloser zu definieren.
Es gibt einen ganz besonderen Interface-Typ in Go: den leeren Interface-Typ, der keine Methoden enthält.
Das leere Interface wird so geschrieben: interface{}.
In Go 1.18 oder höher kann auch any verwendet werden.
Es wurde als Alias definiert.
Da das leere Interface keine Methoden hat, implementiert es jeder Typ implizit. Das ist hilfreich, um eine Funktion zu definieren, die generisch jeden Wert akzeptieren kann. In diesem Fall verwendet der Funktionsparameter den leeren Interface-Typ.
Zum Beispiel:
func AnythingGoes(i interface{}) string {
if i == 0 {
return "zero"
}
if i == "" {
return "empty string"
}
return "something else"
}