Génériques et paramètres de type
Comprenez comment écrire des classes et des méthodes génériques afin de créer des composants réutilisables tout en garantissant la sécurité des types.
Génériques et paramètres de type est une leçon Scala for Backend Engineering & Functional Programming gratuite sur CoddyKit. Ceci est la leçon 1 sur 3. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage Scala for Backend Engineering & Functional Programming, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours Scala for Backend Engineering & Functional Programming comprend 3 leçons au total.
Que sont les types génériques ?
Imaginez que vous deviez créer un conteneur capable de contenir n'importe quel type d'élément, comme une chaîne de caractères, un entier ou un objet personnalisé. Sans les types génériques, vous devriez soit écrire un conteneur distinct pour chaque type, ce qui entraînerait du code répétitif, soit utiliser un type très général comme Any.
Les types génériques vous permettent d'écrire du code flexible et réutilisable qui fonctionne avec différents types de données, tout en garantissant la sûreté des types lors de la compilation. C'est comme un plan dans lequel vous pouvez intégrer différents matériaux ultérieurement !
Le piège du type `Any`
Utiliser Any comme espace réservé pour les types peut poser problème. Même si cela vous permet de stocker n'importe quelle valeur, vous perdez les informations relatives au type précis. Vous devez alors souvent convertir l'élément vers son type d'origine, ce qui peut provoquer des erreurs et entraîner des plantages à l'exécution si la conversion est incorrecte.
Essayez d'exécuter cet exemple et réfléchissez aux problèmes potentiels :
class ItemHolder(val item: Any)
object Main {
def main(args: Array[String]): Unit = {
val stringHolder = new ItemHolder("Hello Scala")
val numberHolder = new ItemHolder(123)
// We need to cast, and it's unsafe if we get it wrong
val retrievedString = stringHolder.item.asInstanceOf[String]
println(s"Retrieved String: $retrievedString")
// If we uncomment the line below, it compiles but will crash at runtime!
// val wrongType = numberHolder.item.asInstanceOf[String]
// println(s"Wrong Type: $wrongType")
}
}Présentation des paramètres de type
Les types génériques résolvent le piège du type Any en introduisant des paramètres de type. Ceux-ci servent d'espaces réservés pour les types réels que vous indiquez lorsque vous créez une instance d'une classe ou appelez une méthode.
En Scala, les paramètres de type sont définis à l'aide de crochets ([]) après le nom de la classe ou de la méthode, généralement avec des lettres majuscules isolées comme T, A, B, etc. Par exemple, class Box[T] déclare une classe générique Box qui accepte un paramètre de type T.
Construire une boîte générique
Créons une classe générique simple appelée Box, capable de contenir n'importe quel type de contenu. Remarquez comment T est utilisé dans toute la définition de la classe pour représenter le type du contenu.
Nous pouvons ainsi créer des boîtes pour des chaînes de caractères, des entiers ou tout autre type, sans écrire une classe distincte pour chacun.
class Box[T](val content: T) {
def get: T = content
override def toString: String = s"Box($content)"
}
object Main {
def main(args: Array[String]): Unit = {
val stringBox = new Box("Scala is fun!")
val intBox = new Box(42)
val booleanBox = new Box(true)
println(stringBox)
println(intBox)
println(booleanBox)
}
}Sûreté des types avec les génériques
Le principal avantage des types génériques est la sûreté des types lors de la compilation. Lorsque vous créez une Box[String], le compilateur sait qu'elle ne doit contenir que des chaînes de caractères. Si vous essayez d'y placer un entier ou de récupérer son contenu en tant qu'entier, le compilateur détecte immédiatement l'erreur.
Cela évite de nombreuses erreurs courantes à l'exécution qui surviennent avec des approches moins sûres du point de vue des types, comme l'utilisation de Any.
class Box[T](val content: T) {
def get: T = content
}
object Main {
def main(args: Array[String]): Unit = {
val myStringBox: Box[String] = new Box("CoddyKit")
val myIntBox: Box[Int] = new Box(100)
// This works, type is String
val s: String = myStringBox.get
println(s"String from box: $s")
// This works, type is Int
val i: Int = myIntBox.get
println(s"Int from box: $i")
// The compiler prevents this line from compiling:
// val wrong: String = myIntBox.get // Type mismatch error!
}
}Les génériques s'appliquent aussi aux méthodes !
Tout comme les classes, les méthodes individuelles peuvent également être génériques. Une méthode générique peut accepter des paramètres de type qui lui sont propres, ce qui lui permet de fonctionner avec différents types sans que toute la classe ait besoin d'être générique.
C'est utile lorsqu'une opération donnée peut s'appliquer à plusieurs types, mais que la classe elle-même n'est pas un conteneur générique.
Une méthode `printPair` flexible
Voici un exemple de méthode générique printPair. Elle accepte deux paramètres de type, A et B, ce qui lui permet d'afficher une paire composée de deux types quelconques. Remarquez que les paramètres de type sont déclarés juste après le nom de la méthode.
La méthode est ainsi très réutilisable pour différentes combinaisons de données.
object Util {
def printPair[A, B](first: A, second: B): Unit = {
println(s"Pair: ($first, $second)")
println(s"Type of first: ${first.getClass.getName}")
println(s"Type of second: ${second.getClass.getName}")
}
}
object Main {
def main(args: Array[String]): Unit = {
Util.printPair("Hello", 123)
Util.printPair(true, List(1, 2, 3))
Util.printPair(4.5, 'C')
}
}Inférence intelligente des types
L'une des fonctionnalités puissantes de Scala est son inférence des types. Lorsque vous utilisez des classes ou des méthodes génériques, vous n'avez souvent pas besoin d'indiquer explicitement les paramètres de type. Le compilateur de Scala est suffisamment intelligent pour les déduire à partir des arguments que vous fournissez.
Votre code est ainsi plus concis et plus facile à lire, sans compromettre la sûreté des types.
class Container[T](val item: T) {
def getItem: T = item
}
object Main {
def main(args: Array[String]): Unit = {
// Scala infers T as String
val stringContainer = new Container("Inferred String")
println(s"Content: ${stringContainer.getItem}, Type: ${stringContainer.getItem.getClass.getName}")
// Scala infers T as Double
val doubleContainer = new Container(3.14)
println(s"Content: ${doubleContainer.getItem}, Type: ${doubleContainer.getItem.getClass.getName}")
// You can also specify explicitly, but often not needed
val explicitIntContainer: Container[Int] = new Container(500)
println(s"Content: ${explicitIntContainer.getItem}, Type: ${explicitIntContainer.getItem.getClass.getName}")
}
}Restreindre les types génériques avec des bornes
Parfois, vous souhaitez qu'une classe ou une méthode générique ne fonctionne qu'avec des types dotés de certaines capacités. Par exemple, une méthode qui additionne des nombres ne devrait accepter que des types numériques.
Les bornes de type permettent de restreindre les types utilisables comme paramètres de type. Une borne supérieure (<:) signifie que le paramètre de type doit être un sous-type d'un type donné. Par exemple, [T <: Animal] signifie que T doit être Animal ou un sous-type de Animal.
class Animal { def speak(): String = "..." }
class Dog extends Animal { override def speak(): String = "Woof!" }
class Cat extends Animal { override def speak(): String = "Meow!" }
// This box can only hold types that are Animal or its subtypes
class AnimalShelter[T <: Animal](val animal: T) {
def getAnimalSound: String = animal.speak()
}
object Main {
def main(args: Array[String]): Unit = {
val dogShelter = new AnimalShelter(new Dog())
println(s"Dog says: ${dogShelter.getAnimalSound}")
val catShelter = new AnimalShelter(new Cat())
println(s"Cat says: ${catShelter.getAnimalSound}")
// This would NOT compile because Car is not an Animal:
// class Car
// val carShelter = new AnimalShelter(new Car())
}
}Exercice sur les classes génériques
Il est temps de vérifier votre compréhension des types génériques !
Lisez attentivement les affirmations suivantes et sélectionnez toutes celles qui sont vraies concernant les classes et les méthodes génériques en Scala.
Types génériques : points essentiels
Dans cette leçon, nous avons étudié les types génériques de Scala, une fonctionnalité puissante pour écrire du code flexible et sûr du point de vue des types.
- Les types génériques permettent aux classes et aux méthodes de fonctionner avec différents types.
- Ils utilisent des paramètres de type (par exemple,
[T]) comme espaces réservés pour les types réels. - Les types génériques garantissent la sûreté des types lors de la compilation et évitent les erreurs à l'exécution.
- L'inférence des types de Scala rend souvent le code générique plus concis.
- Les bornes de type (par exemple,
<:) peuvent restreindre les paramètres de type à certains sous-types.
Grâce aux types génériques, vous pouvez écrire des composants réutilisables, robustes et adaptables à différents types de données, ce qui rend votre code Scala plus puissant et moins sujet aux erreurs.
Apprends Scala avec un tuteur IA — gratuit
Écris et exécute du vrai code dans ton navigateur, obtiens de l'aide instantanée d'un tuteur IA disponible 24h/24, et reprends là où tu t'es arrêté sur le web ou dans l'app.
- Cours
- 39
- Leçons
- 143
Questions Fréquemment Posées
La leçon « Génériques et paramètres de type » est-elle gratuite ?
Oui — le texte complet de « Génériques et paramètres de type » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours Scala for Backend Engineering & Functional Programming, passe à CoddyKit PRO. Le cours Scala for Backend Engineering & Functional Programming comprend 3 leçons au total.
Qu'est-ce que j'apprendrai dans « Génériques et paramètres de type » ?
Comprenez comment écrire des classes et des méthodes génériques afin de créer des composants réutilisables tout en garantissant la sécurité des types. Tu pratiques Scala for Backend Engineering & Functional Programming avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.
Dois-je avoir de l'expérience pour commencer Scala for Backend Engineering & Functional Programming ?
Aucune expérience préalable n'est requise. Scala for Backend Engineering & Functional Programming sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 1 sur 3.
Combien de temps prend la leçon « Génériques et paramètres de type » ?
La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.
Peux-tu écrire et exécuter du code dans cette leçon Scala for Backend Engineering & Functional Programming ?
Oui. Chaque leçon Scala for Backend Engineering & Functional Programming inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.
Toutes les leçons de ce cours
- Génériques et paramètres de type
- Variance : covariance et contravariance
- Classes de types et implicites