Leere Schnittstelle (any)
Teil des Abschnitts Objektorientierte Programmierung der GO-Journey von Coddy. Lektion 28 von 107.
Das leere Interface ist ein Interface mit null Methoden. Da jeder Typ in Go mindestens null Methoden hat, erfüllt jeder Typ automatisch das leere Interface. Dadurch kann es Werte jedes Typs aufnehmen.
var anything interface{}
anything = 42
anything = "hello"
anything = true
anything = []int{1, 2, 3}Ab Go 1.18 wurde das Schlüsselwort any als Alias für interface{} eingeführt. Sie sind vollständig austauschbar, aber any ist leichter zu lesen.
var value any = "Go is awesome"Die leere Schnittstelle ist nützlich, wenn du Werte unbekannter oder unterschiedlicher Typen verarbeiten musst. Funktionen wie fmt.Println verwenden sie, um Argumente jeden Typs zu akzeptieren:
func PrintAnything(v any) {
fmt.Println("Value:", v)
}
func main() {
PrintAnything(100)
PrintAnything("text")
PrintAnything(3.14)
}Allerdings gibt es einen Kompromiss. Wenn ein Wert in einem leeren Interface gespeichert wird, verlierst du den Zugriff auf seine spezifischen Typinformationen.
Du kannst Methoden nicht direkt aufrufen oder auf Felder zugreifen, ohne zuvor den zugrunde liegenden Typ zu extrahieren. In den nächsten Lektionen wird behandelt, wie du mithilfe von Typassertionen und Typswitches den konkreten Typ aus einem leeren Interface abrufst.
Aufgabe
EinfachWir erstellen einen flexiblen Speichercontainer, der mithilfe des leeren Interfaces Werte beliebigen Typs aufnehmen kann. Du erstellst eine einfache Box, die zeigt, wie any es ermöglicht, verschiedene Typen in derselben Struktur zu speichern.
Du organisierst deinen Code über zwei Dateien:
box.go: Definiere eineBox-Struktur mit einem einzigen FeldContentvom Typany. Erstelle eine KonstruktorfunktionNewBox, die einen Wert beliebigen Typs akzeptiert und einen Zeiger auf eine Box zurückgibt, die diesen Wert enthält. Füge eineDescribe-Methode hinzu, die eine Zeichenkette zurückgibt, aus der hervorgeht, was in der Box gespeichert ist.main.go: Lies verschiedene Werte aus der Eingabe ein, erstelle Boxen mit einer Ganzzahl, einer Zeichenkette und einem booleschen Wert und gib anschließend die Beschreibung jeder Box aus, um zu zeigen, wie derselbe Box-Typ völlig unterschiedliche Werte aufnehmen kann.
Die folgenden Eingaben werden bereitgestellt:
- Zeile 1: Ein ganzzahliger Wert
- Zeile 2: Ein Zeichenkettenwert
- Zeile 3: Ein boolescher Wert (
trueoderfalse)
Deine Describe-Methode sollte eine Zeichenkette in folgendem Format zurückgeben:
Box contains: [value]Wenn beispielsweise 42, hello und true gegeben sind, sollte deine Ausgabe folgendermaßen aussehen:
Box contains: 42
Box contains: hello
Box contains: trueDie wichtigste Erkenntnis ist, dass deine einzelne Box-Struktur eine Ganzzahl, eine Zeichenkette oder einen booleschen Wert speichern kann – alles, weil der Typ any Werte beliebigen Typs akzeptiert. Jede Box enthält etwas anderes, dennoch haben sie alle denselben Box-Typ.
Probier es selbst
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Integer-Wert lesen
line1, _ := reader.ReadString('\n')
intVal, _ := strconv.Atoi(strings.TrimSpace(line1))
// String-Wert lesen
line2, _ := reader.ReadString('\n')
strVal := strings.TrimSpace(line2)
// Boolean-Wert lesen
line3, _ := reader.ReadString('\n')
boolVal, _ := strconv.ParseBool(strings.TrimSpace(line3))
// TODO: Erstelle Boxen für jeden Wert mit NewBox
// TODO: Gib die Beschreibung jeder Box mit der Describe-Methode aus
// Verwende die Variablen, um Kompilierungsfehler zu vermeiden (entferne diese Zeilen bei der Implementierung)
_ = intVal
_ = strVal
_ = boolVal
// Beispiel-Ausgabeformat:
// fmt.Println(box.Describe())
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP in Go
Externe DateienGo-Workspace & ModulePackages & ImportsExportierte vs. nicht exportierte NamenEinführung in OOP mit GoStructs als KlassenMethoden für Structs definierenPointer- vs. Value-ReceiverStruct-InitialisierungKonstruktorfunktionenRückblick – Einfacher Taschenrechner4Schnittstellen
Einführung in SchnittstellenImplizite ImplementierungSchnittstelle als VertragLeere Schnittstelle (any)TypzusicherungTypwechselZusammensetzung von SchnittstellenStringer- und Error-SchnittstellenRückblick – Formenrechner7Kapselung
Exportierte vs. nicht exportierte FelderKapselung auf PaketebeneGetter- und Setter-MethodenInformationsverbergung in GoRückblick – Studierendendatensätze10Generics (Go 1.18+)
Einführung in GenericsTypparameterTypbeschränkungenGenerische StrukturenWorkaround für generische MethodenZusammenfassung – Generische Sammlung2Typen & Structs im Detail
Grundlegende & zusammengesetzte TypenBenutzerdefinierte TypdefinitionenStruct-TagsAnonyme StructsVerschachtelte StructsNullwerte & StandardwerteRückblick – Kontaktbuch5Komposition statt Vererbung
Warum Go keine Vererbung hatGrundlagen der Struct-EinbettungMethoden-PromotionMehrere Structs einbettenEinbettung vs. AggregationVerbergen eingebetteter MethodenRückblick – Mitarbeiterhierarchie8Fehlerbehandlung & OOP
Das Error-InterfaceBenutzerdefinierte FehlertypenError-Wrapping (fmt.Errorf)Sentinel-Fehlererrors.Is() und errors.As()Panic, Defer und RecoverRückblick – Dateiparser3Zeiger & Speicher
Grundlagen von Zeigern in GoZeiger auf StructsÜbergabe per Wert vs. ReferenzDie Funktion new()Garbage Collection in GoRückblick – Verkettete Liste erstellen6Polymorphismus in Go
Polymorphismus über InterfacesDuck-Typing in GoRegeln zur Interface-ErfüllungPolymorphe SammlungenDependency InjectionZusammenfassung – Zahlungsprozessor9Konkurrenz & OOP
Grundlagen der GoroutinesChannels & KommunikationGepufferte vs. ungepufferte ChannelsSelect-Anweisungsync.Mutex & sync.RWMutexsync.WaitGroupThread-sicheres Struct-DesignRückblick – Worker PoolÜbe selbstständig: Online-Go-Compiler