0Pricing
Scala for Backend Engineering & Functional Programming · 课时

不透明类型

零成本抽象

不透明类型 是 CoddyKit 上的免费 Scala for Backend Engineering & Functional Programming 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Scala for Backend Engineering & Functional Programming 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Scala for Backend Engineering & Functional Programming 课程共包含 4 节课。

什么是不透明类型?

不透明类型是 Scala 3 中用于创建零成本抽象的功能。它们在编译时提供独立类型,但编译后会直接使用底层类型,不会产生运行时包装。

  • 具有包装类的类型安全性。
  • 没有额外的分配开销。
object Distances:
  opaque type Meters = Double

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

声明不透明类型

在对象或类中声明不透明类型。在该作用域之外,Meters 和 Double 会被视为不同类型。

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))

不透明性的边界

在定义作用域内,不透明类型与其底层类型可以互换。在外部只能看到不透明类型,因此无法在需要 Meters 的地方误传原始 Double。

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))

添加扩展方法

在同一作用域中定义扩展方法,即可为不透明类型添加行为。调用者可以使用便捷操作,同时底层类型不会被公开。

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)

避免类型混淆

最大的优势是:基于同一底层类型定义的两个不透明类型互不兼容。Meters 不会与 Kilometers 混淆,即使它们都是 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))

带边界的不透明类型

您可以使用 <: 为不透明类型指定上界。这样既能公开部分 API,又能隐藏确切表示方式。

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)

带验证的智能构造

将不透明类型与验证工厂结合起来,确保值始终有效。构造器保持私有,只有经过验证的工厂是公开的。

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))

零运行时开销

与值类或样例类包装器不同,不透明类型在运行时不会创建对象。在 JVM 中,Meters 的 List 实际上就是 double 的列表,因此不会发生装箱。

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)

不透明类型与类型别名

普通的 type X = Y 别名是透明的:X 和 Y 在任何地方都完全可以互换。不透明类型则会在其作用域之外隐藏这种等同性,从而提供真正的类型安全。

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)

不透明类型与样例类包装器

像 case class Meters(value: Double) 这样的样例类包装器同样能提供类型安全,但会分配对象。不透明类型可以提供相同的安全性,同时拥有原始类型的性能。

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)

何时使用不透明类型

如果您希望在性能关键路径或大型集合中使用强领域类型,请使用不透明类型。

  • 区分不同的数量(Meters 与 Seconds)。
  • 强制执行经过验证的不变量。
  • 避免包装器带来的分配开销。
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))

快速检查

测试您对不透明类型的理解。

回顾

您学习了 Scala 3 的不透明类型。

  • opaque type X = Y 会在其作用域之外隐藏等同性。
  • 为 API 提供工厂和 extension 方法。
  • 基于同一底层类型的不同不透明类型彼此不兼容。
  • 通过 <: 指定可选的上界,以公开部分 API。
  • 运行时开销为零,不同于样例类包装器。
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)

常见问题解答

「不透明类型」课时是免费的吗?

是的 — 「不透明类型」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Scala for Backend Engineering & Functional Programming 课程的其余内容,请升级到 CoddyKit PRO。 Scala for Backend Engineering & Functional Programming 课程共包含 4 节课。

「不透明类型」这节课中我会学到什么?

零成本抽象 你通过在浏览器中直接运行的动手代码来练习 Scala for Backend Engineering & Functional Programming,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Scala for Backend Engineering & Functional Programming 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Scala for Backend Engineering & Functional Programming 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。

「不透明类型」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Scala for Backend Engineering & Functional Programming 课中编写并运行代码吗?

能。每节 Scala for Backend Engineering & Functional Programming 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 显著缩进
  2. Scala 3 中的枚举
  3. 不透明类型
  4. 联合类型与交集类型
← 返回 Scala for Backend Engineering & Functional Programming