0Pricing
Scala for Backend Engineering & Functional Programming · Leçon

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 differ

Le 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 class pour la simplicité et opaque type pour 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.

Toutes les leçons de ce cours

  1. Modélisation avec les ADT
  2. Constructeurs intelligents
  3. Nouveaux types
  4. Composition des domaines
← Retour à Scala for Backend Engineering & Functional Programming