Introduction aux génériques
Fait partie de la section Programmation Orientée Objet du Journey GO de Coddy. Leçon 67 sur 107.
Avant Go 1.18, écrire du code réutilisable pour différents types impliquait soit de dupliquer les fonctions, soit d’utiliser l’interface vide any avec des assertions de type. Les génériques résolvent ce problème en vous permettant d’écrire des fonctions et des types qui fonctionnent avec plusieurs types tout en conservant une sécurité de typage complète.
Imagine que tu cherches la valeur minimale dans une tranche. Sans les génériques, tu aurais besoin de fonctions distinctes pour chaque type :
func MinInt(values []int) int {
min := values[0]
for _, v := range values {
if v < min {
min = v
}
}
return min
}
func MinFloat64(values []float64) float64 {
min := values[0]
for _, v := range values {
if v < min {
min = v
}
}
return min
}Avec les génériques, tu écris la logique une seule fois en utilisant un paramètre de type entre crochets :
func Min[T int | float64](values []T) T {
min := values[0]
for _, v := range values {
if v < min {
min = v
}
}
return min
}Le [T int | float64] déclare un paramètre de type T qui peut être soit int, soit float64. Vous pouvez maintenant appeler Min([]int{3, 1, 2}) ou Min([]float64{3.5, 1.2}) avec la même fonction. Le compilateur vérifie les types à la compilation, ce qui vous garantit la sécurité sans surcharge à l’exécution.
Dans les prochaines leçons, nous étudierons en détail les paramètres de type, les contraintes et les structures génériques.
Défi
FacileConstruisons un analyseur de scores qui fonctionne avec des scores entiers et à virgule flottante grâce aux génériques ! Tu vas créer une seule fonction générique qui peut trouver la valeur maximale d’une tranche, ce qui élimine le besoin de code en double pour différents types numériques.
Tu vas répartir ton code sur deux fichiers :
analyzer.go: définis ta fonction d’analyse générique.Crée une fonction générique
Max[T int | float64](values []T) Tqui trouve et renvoie la valeur maximale d’une tranche. La fonction doit fonctionner avec les typesintetfloat64grâce au paramètre de typeT.Crée également une fonction générique
Sum[T int | float64](values []T) Tqui calcule et renvoie la somme de toutes les valeurs d’une tranche.main.go: lis les données d’entrée et montre tes fonctions génériques avec différents types.Lis un indicateur de type (
intoufloat), puis un nombre suivi d’autant de valeurs. Analyse les valeurs selon le type, appelleMaxetSumavec le type approprié, puis affiche les résultats.Pour une entrée entière, affiche :
Max (int): [value] Sum (int): [value]Pour une entrée à virgule flottante, affiche avec une décimale :
Max (float): [value] Sum (float): [value]
Les entrées suivantes seront fournies :
- Ligne 1 : indicateur de type (
intoufloat) - Ligne 2 : nombre de valeurs (entier)
- Lignes suivantes : une valeur par ligne
Par exemple, si l’entrée est :
int
4
15
8
23
11Ta sortie doit être :
Max (int): 23
Sum (int): 57Et si l’entrée est :
float
3
4.5
9.2
6.8Ta sortie doit être :
Max (float): 9.2
Sum (float): 20.5Remarque comme les mêmes fonctions génériques traitent les données entières et à virgule flottante : le paramètre de type [T int | float64] permet au compilateur de générer du code sûr en matière de types pour chaque utilisation, alors que tu n’écris la logique qu’une seule fois.
Essayez vous-même
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Lire l’indicateur de type
typeStr, _ := reader.ReadString('\n')
typeStr = strings.TrimSpace(typeStr)
// Lire le nombre d’éléments
countStr, _ := reader.ReadString('\n')
count, _ := strconv.Atoi(strings.TrimSpace(countStr))
if typeStr == "int" {
// Lire les valeurs entières
values := make([]int, count)
for i := 0; i < count; i++ {
line, _ := reader.ReadString('\n')
values[i], _ = strconv.Atoi(strings.TrimSpace(line))
}
// TODO: Appeler Max et Sum avec une tranche d’entiers
// TODO: Afficher les résultats au format "Max (int): [value]" et "Sum (int): [value]"
} else if typeStr == "float" {
// Lire les valeurs à virgule flottante
values := make([]float64, count)
for i := 0; i < count; i++ {
line, _ := reader.ReadString('\n')
values[i], _ = strconv.ParseFloat(strings.TrimSpace(line), 64)
}
// TODO: Appeler Max et Sum avec une tranche de float64
// TODO: Afficher les résultats avec une décimale au format "Max (float): [value]" et "Sum (float): [value]"
}
}
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 et non exportésIntroduction à la POO en GoStructs comme classesDéfinir des méthodes sur des structsRécepteurs pointeurs ou par valeurInitialisation des structsFonctions constructeursRécapitulatif - Calculatrice simple4Interfaces
Introduction aux interfacesImplémentation impliciteL’interface comme contratInterface vide (any)Assertion de typeCommutation de typeComposition d’interfacesInterfaces Stringer et ErrorRécapitulatif - Calculateur de formes7Encapsulation
Champs exportés vs non exportésEncapsulation au niveau du packageMéthodes Getter et SetterDissimulation de l’information en GoRécapitulatif – Fiches d’étudiants10Génériques (Go 1.18+)
Introduction aux génériquesParamètres de typeContraintes de typeStructures génériquesSolution de contournement pour les méthodes génériquesRécapitulatif - Collection générique2Plongée approfondie dans les types et les structs
Types de base et compositesDéfinitions de types personnalisésTags de structStructs anonymesStructs imbriquésValeurs zéro et valeurs par défautRécapitulatif - Carnet de contacts5La composition plutôt que l’héritage
Pourquoi Go n’a pas d’héritageBases de l’inclusion de structsPromotion des méthodesInclure plusieurs structsInclusion ou agrégationMasquage des méthodes inclusesRécapitulatif : hiérarchie des employés8Gestion des erreurs et POO
L’interface errorTypes d’erreurs personnalisésEnrobage des erreurs (fmt.Errorf)Erreurs sentinelleserrors.Is() et errors.As()Panic, Defer et RecoverRécapitulatif - Analyseur de fichiers11Bibliothèque standard et POO
io.Reader et io.Writersort.InterfaceInterface fmt.Stringerencoding/json avec des structsInterface http.HandlerRécapitulatif - Modèles d’API REST14Modèles de conception – Partie 2
Modèle CommandeModèle AdaptateurModèle DécorateurModèle Méthode modèleModèle ÉtatModèle CompositeMiddleware comme décorateur3Pointeurs et mémoire
Notions de base des pointeurs en GoPointeurs vers des structuresPassage par valeur ou par référenceLa fonction new()Garbage collection en GoRécapitulatif - Constructeur de listes chaînées6Polymorphisme en Go
Polymorphisme via les interfacesDuck typing en GoRègles de satisfaction des interfacesCollections polymorphesInjection de dépendancesRécapitulatif – Processeur de paiements9Concurrence et POO
Bases des GoroutinesCanaux et communicationCanaux tamponnés ou non tamponnésInstruction selectsync.Mutex et sync.RWMutexsync.WaitGroupConception de structures thread-safeRécapitulatif - Pool de workersEntraînez-vous par vous-même : Compilateur Go en ligne