Generische Strukturen
Teil des Abschnitts Objektorientierte Programmierung der GO-Journey von Coddy. Lektion 70 von 107.
Genau wie Funktionen können auch Strukturen Typparameter haben. Eine generische Struktur ermöglicht es dir, Datenstrukturen zu erstellen, die mit jedem Typ funktionieren und dabei die Typsicherheit gewährleisten. Das ist ideal zum Erstellen wiederverwendbarer Container wie Stapel, Warteschlangen oder Schlüssel-Wert-Paare.
Um eine generische Struktur zu definieren, platziere den Typparameter nach dem Namen der Struktur:
type Box[T any] struct {
Value T
}
func main() {
intBox := Box[int]{Value: 42}
strBox := Box[string]{Value: "hello"}
fmt.Println(intBox.Value) // 42
fmt.Println(strBox.Value) // hello
}Im Gegensatz zu generischen Funktionen musst du den Typ beim Erstellen einer Instanz einer generischen Struktur ausdrücklich angeben. Go kann den Typ nicht allein aus dem Kontext ableiten.
Generische Structs können mehrere Typparameter haben, was sie ideal für Strukturen wie Paare oder Maps macht:
type Pair[K, V any] struct {
Key K
Value V
}
p := Pair[string, int]{Key: "age", Value: 25}
fmt.Printf("%s: %d\n", p.Key, p.Value) // age: 25Sie können auch Einschränkungen anwenden, um sicherzustellen, dass die gespeicherten Typen bestimmte Operationen unterstützen:
type NumberBox[T int | float64] struct {
Value T
}
nb := NumberBox[float64]{Value: 3.14}Generische Structs bilden die Grundlage für den Aufbau typsicherer, wiederverwendbarer Datenstrukturen in Go. In der nächsten Lektion werden Sie lernen, wie Sie Methoden für diese generischen Typen definieren.
Aufgabe
EinfachErstellen wir ein generisches Inventarsystem, das verschiedene Arten von Elementen verfolgen kann! Du erstellst wiederverwendbare Containerstrukturen, die mit jedem Typ funktionieren und dabei vollständige Typsicherheit gewährleisten.
Du organisierst deinen Code über zwei Dateien:
inventory.go: Definiere deine generischen Containerstrukturen.Erstelle eine generische Struktur
Item[T any]mit zwei Feldern:Name(string) undData(vom Typ T). Dies stellt ein beliebiges Element mit zugehörigen Daten eines flexiblen Typs dar.Erstelle eine generische Struktur
Container[T any]mit einem einzigen FeldItems, das ein Slice ausItem[T]enthält. Dieser Container kann mehrere Elemente desselben Datentyps speichern.Erstelle eine eingeschränkte generische Struktur
PricedItem[T int | float64]mit drei Feldern:Name(string),Quantity(int) undPrice(vom Typ T). Die Einschränkung stellt sicher, dass Preise immer numerisch sind.Implementiere eine Funktion
NewContainer[T any]() *Container[T], die einen Zeiger auf einen leeren Container erstellt und zurückgibt.Implementiere eine Methode
Addfür*Container[T], die einen Namen (string) und Daten (T) entgegennimmt, ein Item erstellt und es an den Items-Slice des Containers anhängt.Implementiere eine Methode
CountfürContainer[T], die die Anzahl der Elemente im Container zurückgibt.main.go: Veranschauliche deine generischen Strukturen mit verschiedenen Typen.Lies einen Elementtyp (
string,intoderpriced) ein und anschließend eine Anzahl gefolgt von den Elementdetails.Für den Typ
string: Lies Paare aus Namen und String-Daten ein. Erstelle einenContainer[string], füge alle Elemente hinzu und gib anschließend jedes Element als[Name]: [Data]aus, gefolgt von der Gesamtanzahl.Für den Typ
int: Lies Paare aus Namen und Ganzzahldaten ein. Erstelle einenContainer[int], füge alle Elemente hinzu und gib anschließend jedes Element als[Name]: [Data]aus, gefolgt von der Gesamtanzahl.Für den Typ
priced: Lies Tripel aus Name, Menge und Preis (als float) ein. Erstelle direkt Instanzen vonPricedItem[float64]und gib jedes als[Name] x[Quantity] @ [Price]aus, wobei der Preis mit einer Dezimalstelle angezeigt wird.Gib die Anzahlzeile wie folgt aus:
Total items: [count]
Die folgenden Eingaben werden bereitgestellt:
- Zeile 1: Elementtyp (
string,intoderpriced) - Zeile 2: Anzahl der Elemente (Ganzzahl)
- Folgende Zeilen: Elementdetails abhängig vom Typ
Zum Beispiel bei folgender Eingabe:
string
3
Book
Fiction Novel
Pen
Blue Ink
Notebook
Lined PaperDeine Ausgabe sollte wie folgt aussehen:
Book: Fiction Novel
Pen: Blue Ink
Notebook: Lined Paper
Total items: 3Und bei folgender Eingabe:
int
2
Apples
50
Oranges
30Deine Ausgabe sollte wie folgt aussehen:
Apples: 50
Oranges: 30
Total items: 2Und bei folgender Eingabe:
priced
2
Widget
10
19.99
Gadget
5
49.50Deine Ausgabe sollte wie folgt aussehen:
Widget x10 @ 19.9
Gadget x5 @ 49.5
Total items: 2Beachte, wie dieselbe Container-Struktur nahtlos sowohl mit Strings als auch mit Ganzzahlen funktioniert, während PricedItem eine Einschränkung verwendet, um sicherzustellen, dass für Preise nur numerische Typen verwendet werden können. Du musst beim Erstellen von Instanzen dieser generischen Strukturen den Typparameter ausdrücklich angeben.
Probier es selbst
package main
import (
"bufio"
"fmt"
"os"
"strconv"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// Elementtyp lesen
scanner.Scan()
itemType := scanner.Text()
// Anzahl lesen
scanner.Scan()
count, _ := strconv.Atoi(scanner.Text())
switch itemType {
case "string":
// TODO: Erstelle einen Container[string] mit NewContainer
// TODO: Lies 'count' Elemente (jedes hat den Namen in einer Zeile, die Daten in der nächsten Zeile)
// TODO: Füge jedes Element zum Container hinzu
// TODO: Gib jedes Element als "[Name]: [Data]" aus
// TODO: Gib "Total items: [count]" mit der Count-Methode aus
case "int":
// TODO: Erstelle einen Container[int] mit NewContainer
// TODO: Lies 'count' Elemente (jedes hat den Namen in einer Zeile, Integer-Daten in der nächsten Zeile)
// TODO: Füge jedes Element zum Container hinzu
// TODO: Gib jedes Element als "[Name]: [Data]" aus
// TODO: Gib "Total items: [count]" mit der Count-Methode aus
case "priced":
// TODO: Erstelle einen Slice, um PricedItem[float64]-Instanzen zu halten
// TODO: Lies 'count' Elemente (jedes hat Name, Menge und Preis in separaten Zeilen)
// TODO: Erstelle ein PricedItem[float64] für jedes und füge es zum Slice hinzu
// TODO: Gib jedes Element als "[Name] x[Quantity] @ [Price]" aus, wobei der Preis eine Dezimalstelle anzeigt
// TODO: Gib "Total items: [count]" aus
}
_ = scanner // Verwende scanner, um die Eingabe zu lesen
_ = count // Verwende count für die Schleife
}
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