0Pricing
Scala for Backend Engineering & Functional Programming · Lezione

Tipi opachi

Astrazioni a costo zero

Tipi opachi è una lezione Scala for Backend Engineering & Functional Programming gratuita su CoddyKit. Questa è la lezione 3 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento Scala for Backend Engineering & Functional Programming, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Scala for Backend Engineering & Functional Programming include 4 lezioni in totale.

Che cosa sono i tipi opachi?

I tipi opachi sono una funzionalità di Scala 3 per creare astrazioni a costo zero. Forniscono un tipo distinto in fase di compilazione, ma vengono compilati nel tipo sottostante senza alcun wrapper a runtime.

  • Sicurezza dei tipi pari a quella di una classe wrapper.
  • Nessun costo di allocazione.
object Distances:
  opaque type Meters = Double

object Main:
  def main(args: Array[String]): Unit =
    println("Opaque types compile to their underlying type")

Dichiarare un tipo opaco

Dichiari un tipo opaco all'interno di un oggetto o di una classe. Al di fuori di quell'ambito, Meters e Double vengono trattati come tipi diversi.

object Distances:
  opaque type Meters = Double
  def meters(d: Double): Meters = d
  def toDouble(m: Meters): Double = m

object Main:
  def main(args: Array[String]): Unit =
    val d = Distances.meters(5.0)
    println(Distances.toDouble(d))

Il confine dell'opacità

All'interno dell'ambito in cui viene definito, il tipo opaco e il tipo sottostante sono intercambiabili. All'esterno è visibile solo il tipo opaco, quindi non è possibile passare accidentalmente un Double grezzo dove è richiesto Meters.

object Distances:
  opaque type Meters = Double
  def apply(d: Double): Meters = d
  def show(m: Meters): String = s"$m m"

object Main:
  def main(args: Array[String]): Unit =
    val m = Distances(10.0)
    println(Distances.show(m))

Aggiungere metodi di estensione

Assegni un comportamento ai tipi opachi con metodi di estensione definiti nello stesso ambito. Chi li usa ottiene operazioni pratiche senza esporre il tipo sottostante.

object Distances:
  opaque type Meters = Double
  def apply(d: Double): Meters = d
  extension (m: Meters)
    def +(other: Meters): Meters = m + other
    def value: Double = m

object Main:
  def main(args: Array[String]): Unit =
    val total = Distances(3.0) + Distances(4.0)
    println(total.value)

Prevenire gli errori di tipo

Il vantaggio principale è che due tipi opachi basati sullo stesso tipo sottostante sono incompatibili. Meters non può essere confuso con Kilometers, anche se entrambi sono Double.

object Units:
  opaque type Meters = Double
  opaque type Kilometers = Double
  def m(d: Double): Meters = d
  def km(d: Double): Kilometers = d
  def toMeters(k: Kilometers): Meters = k * 1000

object Main:
  def main(args: Array[String]): Unit =
    val far = Units.km(2.0)
    println(Units.toMeters(far))

Tipi opachi con limiti

È possibile assegnare a un tipo opaco un limite superiore con <:. In questo modo si espone parte dell'API pubblica mantenendo nascosta la rappresentazione esatta.

object Ids:
  opaque type UserId <: Int = Int
  def apply(i: Int): UserId = i

object Main:
  def main(args: Array[String]): Unit =
    val id = Ids(99)
    println(id + 1)

Costruzione sicura con validazione

Combini i tipi opachi con una factory di validazione per garantire che i valori siano sempre validi. Il costruttore rimane privato; solo la factory validata è pubblica.

object Ages:
  opaque type Age = Int
  def of(i: Int): Option[Age] =
    if i >= 0 && i < 150 then Some(i) else None
  extension (a: Age) def value: Int = a

object Main:
  def main(args: Array[String]): Unit =
    println(Ages.of(30).map(_.value))
    println(Ages.of(-1))

Costo di runtime nullo

A differenza di un wrapper basato su una value class o su una case class, un tipo opaco non crea alcun oggetto a runtime. Una List di Meters è letteralmente una lista di double nella JVM, quindi non c'è boxing.

object Temps:
  opaque type Celsius = Double
  def c(d: Double): Celsius = d
  extension (t: Celsius) def value: Double = t

object Main:
  def main(args: Array[String]): Unit =
    val readings = List(Temps.c(20.0), Temps.c(21.5))
    println(readings.map(_.value).sum)

Tipo opaco e alias di tipo

Un semplice alias type X = Y è trasparente: X e Y sono completamente intercambiabili ovunque. Un tipo opaco nasconde questa equivalenza al di fuori del proprio ambito, offrendo una reale sicurezza del tipo.

object Demo:
  type Name = String          // transparent alias
  opaque type Email = String  // opaque
  def email(s: String): Email = s
  extension (e: Email) def raw: String = e

object Main:
  def main(args: Array[String]): Unit =
    val e = Demo.email("a@b.com")
    println(e.raw)

Tipo opaco e wrapper con case class

Un wrapper con case class come case class Meters(value: Double) offre anch'esso sicurezza del tipo, ma alloca un oggetto. I tipi opachi garantiscono la stessa sicurezza con le prestazioni del tipo sottostante.

object Money:
  opaque type Cents = Long
  def cents(n: Long): Cents = n
  extension (c: Cents)
    def +(o: Cents): Cents = c + o
    def value: Long = c

object Main:
  def main(args: Array[String]): Unit =
    val total = Money.cents(150) + Money.cents(50)
    println(total.value)

Quando usare i tipi opachi

Utilizzi i tipi opachi quando desidera una tipizzazione forte del dominio nei percorsi critici o in collezioni di grandi dimensioni.

  • Distinguere le quantità (Meters e Seconds).
  • Imporre invarianti validate.
  • Evitare il sovraccarico di allocazione dei wrapper.
object Geo:
  opaque type Latitude = Double
  def lat(d: Double): Option[Latitude] =
    if d >= -90 && d <= 90 then Some(d) else None
  extension (l: Latitude) def value: Double = l

object Main:
  def main(args: Array[String]): Unit =
    println(Geo.lat(41.0).map(_.value))

Verifica rapida

Verifichi la sua comprensione dei tipi opachi.

Riepilogo

Ha imparato a usare i tipi opachi di Scala 3.

  • opaque type X = Y nasconde l'equivalenza al di fuori del proprio ambito.
  • Fornisca factory e metodi extension per l'API.
  • Tipi opachi diversi basati sullo stesso tipo sottostante sono incompatibili.
  • I limiti superiori opzionali con <: espongono una parte dell'API.
  • Costo di runtime nullo, a differenza dei wrapper con case class.
object Ids:
  opaque type OrderId = String
  def apply(s: String): OrderId = s
  extension (o: OrderId) def raw: String = o

object Main:
  def main(args: Array[String]): Unit =
    val id = Ids("ORD-1")
    println(id.raw)

Domande Frequenti

La lezione «Tipi opachi» è gratuita?

Sì — il testo completo di «Tipi opachi» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso Scala for Backend Engineering & Functional Programming, passa a CoddyKit PRO. Il corso Scala for Backend Engineering & Functional Programming include 4 lezioni in totale.

Cosa imparerò in «Tipi opachi»?

Astrazioni a costo zero Eserciti Scala for Backend Engineering & Functional Programming con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare Scala for Backend Engineering & Functional Programming?

Non è richiesta alcuna esperienza precedente. Scala for Backend Engineering & Functional Programming su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 3 di 4.

Quanto tempo richiede la lezione «Tipi opachi»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione Scala for Backend Engineering & Functional Programming?

Sì. Ogni lezione Scala for Backend Engineering & Functional Programming include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Indentazione significativa
  2. Enum in Scala 3
  3. Tipi opachi
  4. Tipi unione e intersezione
← Torna a Scala for Backend Engineering & Functional Programming