Structures génériques
Fait partie de la section Programmation Orientée Objet du Journey GO de Coddy — leçon 70 sur 107.
Tout comme les fonctions, les structs peuvent également avoir des paramètres de type. Une struct générique vous permet de créer des structures de données qui fonctionnent avec n'importe quel type tout en maintenant la sécurité des types. C'est parfait pour construire des conteneurs réutilisables comme des piles, des files d'attente ou des paires clé-valeur.
Pour définir une structure générique, placez le paramètre de type après le nom de la structure :
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
}Contrairement aux fonctions génériques, vous devez spécifier explicitement le type lors de la création d'une instance d'une structure générique. Go ne peut pas inférer le type uniquement à partir du contexte.
Les structures génériques peuvent avoir plusieurs paramètres de type, ce qui les rend idéales pour des structures comme des paires ou des maps :
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: 25Vous pouvez également appliquer des contraintes pour garantir que les types stockés prennent en charge des opérations spécifiques :
type NumberBox[T int | float64] struct {
Value T
}
nb := NumberBox[float64]{Value: 3.14}Les structures génériques constituent le fondement de la création de structures de données réutilisables et de type sûr en Go. Dans la prochaine leçon, vous apprendrez comment définir des méthodes sur ces types génériques.
Défi
FacileConstruisons un système d'inventaire générique capable de suivre différents types d'articles ! Vous allez créer des structures de conteneurs réutilisables qui fonctionnent avec n'importe quel type tout en maintenant une sécurité de type complète.
Vous organiserez votre code sur deux fichiers :
inventory.go: Définissez vos structures de conteneurs génériques.Créez une structure générique
Item[T any]avec deux champs :Name(string) etData(de type T). Cela représente n'importe quel article avec des données associées d'un type flexible.Créez une structure générique
Container[T any]avec un seul champItemsqui contient une tranche (slice) deItem[T]. Ce conteneur peut stocker plusieurs articles du même type de données.Créez une structure générique contrainte
PricedItem[T int | float64]avec trois champs :Name(string),Quantity(int), etPrice(de type T). La contrainte garantit que les prix sont toujours numériques.Implémentez une fonction
NewContainer[T any]() *Container[T]qui crée et retourne un pointeur vers un Container vide.Implémentez une méthode
Addsur*Container[T]qui prend un nom (string) et des données (T), crée un Item, et l'ajoute à la tranche Items du conteneur.Implémentez une méthode
CountsurContainer[T]qui retourne le nombre d'articles dans le conteneur.main.go: Démontrez vos structures génériques avec différents types.Lisez un type d'article (
string,int, oupriced), puis lisez un nombre suivi des détails de l'article.Pour le type
string: Lisez des paires de nom et de données de type chaîne. Créez unContainer[string], ajoutez tous les articles, puis affichez chaque article sous la forme[Name]: [Data]suivi du nombre total.Pour le type
int: Lisez des paires de nom et de données entières. Créez unContainer[int], ajoutez tous les articles, puis affichez chaque article sous la forme[Name]: [Data]suivi du nombre total.Pour le type
priced: Lisez des triplets de nom, quantité et prix (en tant que float). Créez des instances dePricedItem[float64]directement et affichez chacune sous la forme[Name] x[Quantity] @ [Price]avec le prix affichant une décimale.Affichez la ligne du total comme ceci :
Total items: [count]
Les entrées suivantes seront fournies :
- Ligne 1 : Type d'article (
string,int, oupriced) - Ligne 2 : Nombre d'articles (entier)
- Lignes suivantes : Détails de l'article selon le type
Par exemple, avec :
string
3
Book
Fiction Novel
Pen
Blue Ink
Notebook
Lined PaperVotre sortie devrait être :
Book: Fiction Novel
Pen: Blue Ink
Notebook: Lined Paper
Total items: 3Et avec :
int
2
Apples
50
Oranges
30Votre sortie devrait être :
Apples: 50
Oranges: 30
Total items: 2Et avec :
priced
2
Widget
10
19.99
Gadget
5
49.50Votre sortie devrait être :
Widget x10 @ 19.9
Gadget x5 @ 49.5
Total items: 2Remarquez comment la même structure Container fonctionne parfaitement avec les chaînes de caractères et les entiers, tandis que PricedItem utilise une contrainte pour garantir que seuls les types numériques peuvent être utilisés pour les prix. Vous devez explicitement spécifier le paramètre de type lors de la création d'instances de ces structures génériques.
Aide-mémoire
Une structure générique vous permet de créer des structures de données qui fonctionnent avec n'importe quel type tout en maintenant la sécurité des types.
Pour définir une structure générique, placez le paramètre de type après le nom de la structure :
type Box[T any] struct {
Value T
}Lors de la création d'une instance d'une structure générique, vous devez spécifier explicitement le type (Go ne peut pas l'inférer) :
intBox := Box[int]{Value: 42}
strBox := Box[string]{Value: "hello"}Les structures génériques peuvent avoir plusieurs paramètres de type :
type Pair[K, V any] struct {
Key K
Value V
}
p := Pair[string, int]{Key: "age", Value: 25}Vous pouvez appliquer des contraintes pour garantir que les types stockés prennent en charge des opérations spécifiques :
type NumberBox[T int | float64] struct {
Value T
}
nb := NumberBox[float64]{Value: 3.14}Les structures génériques peuvent contenir des champs qui utilisent le paramètre de type, y compris des slices :
type Container[T any] struct {
Items []T
}Vous pouvez définir des méthodes sur des structures génériques et créer des fonctions constructeurs génériques :
func NewContainer[T any]() *Container[T] {
return &Container[T]{Items: []T{}}
}
func (c *Container[T]) Add(item T) {
c.Items = append(c.Items, item)
}Essayez vous-même
package main
import (
"bufio"
"fmt"
"os"
"strconv"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// Lire le type d'élément
scanner.Scan()
itemType := scanner.Text()
// Lire le nombre
scanner.Scan()
count, _ := strconv.Atoi(scanner.Text())
switch itemType {
case "string":
// TODO: Créer un Container[string] en utilisant NewContainer
// TODO: Lire 'count' éléments (chacun a un nom sur une ligne, des données sur la ligne suivante)
// TODO: Ajouter chaque élément au conteneur
// TODO: Afficher chaque élément comme "[Name]: [Data]"
// TODO: Afficher "Total items: [count]" en utilisant la méthode Count
case "int":
// TODO: Créer un Container[int] en utilisant NewContainer
// TODO: Lire 'count' éléments (chacun a un nom sur une ligne, des données entières sur la ligne suivante)
// TODO: Ajouter chaque élément au conteneur
// TODO: Afficher chaque élément comme "[Name]: [Data]"
// TODO: Afficher "Total items: [count]" en utilisant la méthode Count
case "priced":
// TODO: Créer une slice pour contenir des instances de PricedItem[float64]
// TODO: Lire 'count' éléments (chacun a un nom, une quantité et un prix sur des lignes séparées)
// TODO: Créer un PricedItem[float64] pour chacun et l'ajouter à la slice
// TODO: Afficher chaque élément comme "[Name] x[Quantity] @ [Price]" avec le prix affichant une décimale
// TODO: Afficher "Total items: [count]"
}
_ = scanner // Utiliser scanner pour lire l'entrée
_ = count // Utiliser count pour la boucle
}
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Programmation Orientée Objet
1Fondamentaux de la POO en Go
Fichiers externesEspace de travail et Modules GoPackages et ImportsNoms exportés vs non exportésIntroduction à la POO en GoLes Structs comme ClassesDéfinir des méthodes sur les StructsReceveurs par pointeur vs par valeurInitialisation de StructsFonctions constructeursRécapitulatif - Calculatrice simple4Interfaces
Introduction aux interfacesImplémentation impliciteL'interface comme contratInterface vide (any)Assertion de typeType SwitchComposition d'interfacesInterfaces Stringer & ErrorRécapitulatif - Calculateur de formes7Encapsulation
Champs exportés vs non exportésEncapsulation au niveau du packageMéthodes Getter & SetterMasquage d'informations en GoRécapitulatif - Dossiers étudiants10Génériques (Go 1.18+)
Introduction aux génériquesParamètres de typeContraintes de typeStructures génériquesContournement pour les méthodes génériquesRécapitulatif - Collections génériques13Patrons de conception, Partie 1
Introduction aux Design PatternsPatron SingletonPatron FactoryPatron Abstract FactoryPatron ObserverPatron Strategy2Exploration approfondie des Types et Structs
Types de base et compositesDéfinitions de types personnalisésTags de StructStructs anonymesStructs imbriquéesValeurs par défaut et Zero ValuesRécapitulatif - Carnet de contacts5Composition plutôt qu'héritage
Pourquoi Go n'a pas d'héritageLes bases de l'imbrication de structsPromotion de méthodesImbriquer plusieurs structsImbrication vs AgrégationMasquage de méthodes imbriquéesRécapitulatif - Hiérarchie des employés8Gestion des erreurs & POO
L'interface errorTypes d'erreurs personnalisésError Wrapping (fmt.Errorf)Erreurs sentinelleserrors.Is() et errors.As()Panic, Defer et RecoverRécapitulatif - File Parser11Bibliothèque standard et POO
io.Reader & io.WriterL'interface sort.InterfaceL'interface fmt.Stringerencoding/json avec les StructsL'interface http.HandlerRécapitulatif - Modèles d'API REST14Patrons de conception, Partie 2
Patron CommandePatron AdaptateurPatron DécorateurPatron Template MethodPatron ÉtatPatron CompositeMiddleware en tant que Décorateur3Pointeurs et Mémoire
Bases des pointeurs en GoPointeurs vers des structsPassage par valeur vs référenceLa fonction new()Garbage Collection en GoRécapitulatif - Constructeur de liste chaînée6Le polymorphisme en Go
Le polymorphisme via les interfacesLe Duck Typing en GoRègles de satisfaction des interfacesCollections polymorphesInjection de dépendancesRécapitulatif - Processeur de paiement9Concurrence et POO
Bases des GoroutinesChannels et CommunicationChannels avec buffer vs sans bufferL'instruction Selectsync.Mutex et sync.RWMutexsync.WaitGroupConception de structures Thread-SafeRécapitulatif - Worker Pool