Nouveaux types
Enveloppes garantissant la sûreté des types
Nouveaux types est une leçon Scala for Backend Engineering & Functional Programming gratuite sur CoddyKit. Ceci est la leçon 3 sur 4. 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 4 leçons au total.
Enveloppes garantissant les types
Un nouveau type est un type distinct qui enveloppe une seule valeur sous-jacente. Il empêche de confondre des valeurs ayant la même représentation primitive, comme un UserId et un ProductId qui sont tous deux des Int.
case class UserId(value: Int)
case class ProductId(value: Int)
object Main:
def main(args: Array[String]): Unit =
val u = UserId(1)
val p = ProductId(1)
println(u.value == p.value) // values equal, types differLe problème de l’obsession des types primitifs
L’obsession des types primitifs consiste à utiliser excessivement String et Int pour représenter des concepts métier. Elle autorise des absurdités, comme transmettre un e-mail là où un nom est attendu. Les nouveaux types corrigent ce problème en attribuant à chaque concept son propre type.
case class Email(value: String)
case class City(value: String)
object Main:
def greet(c: City): String = s"Welcome to ${c.value}"
def main(args: Array[String]): Unit =
println(greet(City("Berlin")))Nouveaux types avec case class
Le nouveau type le plus simple est une case class à champ unique. Il fournit l’égalité, un toString lisible et la correspondance de motifs immédiatement. Son coût est l’allocation d’un objet par valeur.
case class OrderId(value: String)
object Main:
def main(args: Array[String]): Unit =
val id = OrderId("ORD-42")
println(id)
println(id.value)Nouveaux types opaques
Pour éviter toute allocation, implémentez un nouveau type avec un type opaque. Il se comporte comme le type sous-jacent à l’exécution, tout en étant distinct lors de la compilation.
object Ids:
opaque type UserId = Int
def apply(i: Int): UserId = i
extension (u: UserId) def value: Int = u
object Main:
def main(args: Array[String]): Unit =
val u = Ids(7)
println(u.value)Ajouter un comportement avec des extensions
Donnez des opérations à un nouveau type grâce à des méthodes extension, en n’exposant que celles qui ont un sens pour le concept métier.
object Money:
opaque type Cents = Long
def apply(n: Long): Cents = n
extension (c: Cents)
def +(o: Cents): Cents = c + o
def toDollars: Double = c / 100.0
object Main:
def main(args: Array[String]): Unit =
val total = Money(250) + Money(750)
println(total.toDollars)Nouveaux types dans les signatures de fonctions
Les nouveaux types rendent les signatures explicites et détectent les erreurs d’ordre des arguments lors de la compilation. Vous ne pouvez pas intervertir accidentellement deux paramètres de nouveaux types différents.
case class Width(value: Int)
case class Height(value: Int)
object Main:
def area(w: Width, h: Height): Int = w.value * h.value
def main(args: Array[String]): Unit =
println(area(Width(4), Height(5)))Nouveaux types et collections
Un Map indexé par un nouveau type est plus clair et plus sûr qu’un Map indexé par un Int brut. Le type empêche d’utiliser une clé du mauvais genre.
case class UserId(value: Int)
object Main:
def main(args: Array[String]): Unit =
val names = Map(UserId(1) -> "Ada", UserId(2) -> "Bob")
println(names(UserId(2)))Nouveaux types validés
Associez un nouveau type à un constructeur intelligent afin que l’enveloppe fasse également respecter les invariants. Ici, un NonEmptyString ne peut jamais être vide.
case class NonEmptyString private (value: String)
object NonEmptyString:
def of(s: String): Option[NonEmptyString] =
if s.nonEmpty then Some(NonEmptyString(s)) else None
object Main:
def main(args: Array[String]): Unit =
println(NonEmptyString.of("hi"))
println(NonEmptyString.of(""))Choisir une représentation
Choisissez en fonction de vos besoins.
- case class : la plus simple, compatible avec la correspondance de motifs, mais elle alloue un objet.
- type opaque : sans coût supplémentaire ni boxing, idéale pour les chemins très sollicités et les grandes collections.
object Temp:
opaque type Kelvin = Double
def apply(d: Double): Kelvin = d
extension (k: Kelvin) def value: Double = k
object Main:
def main(args: Array[String]): Unit =
val readings = List(Temp(300.0), Temp(310.5))
println(readings.map(_.value).sum)Éviter les conversions accidentelles
Deux nouveaux types reposant sur la même base ne se convertissent pas implicitement. Pour passer de l’un à l’autre, vous devez écrire une fonction explicite qui documente l’intention.
case class Meters(value: Double)
case class Feet(value: Double)
object Main:
def toFeet(m: Meters): Feet = Feet(m.value * 3.281)
def main(args: Array[String]): Unit =
println(toFeet(Meters(2.0)))Quand utiliser les nouveaux types
Utilisez les nouveaux types dès qu’un type primitif porte une signification métier.
- Identifiants, unités, chaînes formatées.
- Utilisez une case class pour la simplicité et un type opaque pour les performances.
- Ajoutez un constructeur intelligent lorsqu’il existe des invariants.
object Domain:
opaque type Sku = String
def of(s: String): Option[Sku] =
if s.startsWith("SKU-") then Some(s) else None
extension (k: Sku) def raw: String = k
object Main:
def main(args: Array[String]): Unit =
println(Domain.of("SKU-9").map(_.raw))Vérification rapide
Vérifiez votre compréhension des nouveaux types.
Récapitulatif
Vous avez appris les nouveaux types.
- Les nouveaux types enveloppent une valeur sous-jacente dans un type distinct.
- Ils corrigent l’obsession des types primitifs et empêchent les confusions entre arguments.
- Utilisez
case classpour la simplicité etopaque typepour l’absence de coût supplémentaire. - Ajoutez un constructeur intelligent pour les enveloppes validées.
- Les conversions entre nouveaux types sont toujours explicites.
object Ids:
opaque type AccountId = Long
def apply(n: Long): AccountId = n
extension (a: AccountId) def value: Long = a
object Main:
def main(args: Array[String]): Unit =
val acc = Ids(1001L)
println(acc.value)Questions Fréquemment Posées
La leçon « Nouveaux types » est-elle gratuite ?
Oui — le texte complet de « Nouveaux types » 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 4 leçons au total.
Qu'est-ce que j'apprendrai dans « Nouveaux types » ?
Enveloppes garantissant la sûreté 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 3 sur 4.
Combien de temps prend la leçon « Nouveaux types » ?
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.