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

联合类型与交集类型

全新的类型特性

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

新的类型特性

Scala 3 为类型系统加入了联合类型(A | B)和交集类型(A & B)。它们可以在不依赖继承层次结构的情况下,更精确地描述值。

object Main:
  def main(args: Array[String]): Unit =
    println("Union: A | B, Intersection: A & B")

联合类型基础

联合类型 A | B 表示一个值要么是 A,要么是 B。它是类型之间的集合论 OR。

object Main:
  def show(x: Int | String): String = x match
    case i: Int    => s"int $i"
    case s: String => s"str $s"

  def main(args: Array[String]): Unit =
    println(show(42))
    println(show("hi"))

联合类型不需要公共超类型

与 Scala 2 的 Either 不同,联合类型不需要包装。任意两个类型都可以组成联合类型,即使它们没有共同的父类。

object Main:
  def parse(input: Int | Boolean): String =
    input match
      case n: Int     => s"number $n"
      case b: Boolean => s"flag $b"

  def main(args: Array[String]): Unit =
    println(parse(7))
    println(parse(true))

使用联合类型建模错误

联合类型适合在不引入包装类型的情况下表示轻量级错误结果。与枚举或单例结合使用时,代码表达清晰。

case object NotFound

object Main:
  def lookup(id: Int): String | NotFound.type =
    if id == 1 then "Alice" else NotFound

  def main(args: Array[String]): Unit =
    println(lookup(1))
    println(lookup(2))

交集类型基础

交集类型 A & B 表示一个值同时是 A 和 B。它是集合论中的 AND,常用于组合特征。

trait Named:
  def name: String
trait Aged:
  def age: Int

object Main:
  def describe(p: Named & Aged): String =
    s"${p.name} is ${p.age}"

  def main(args: Array[String]): Unit =
    val person = new Named with Aged:
      def name = "Bob"
      def age = 40
    println(describe(person))

交集满足交换律

交集类型的顺序无关紧要:A & B 与 B & A 是同一个类型。这不同于 Scala 2 的复合类型 A with B,后者在解析某些成员时会受到顺序影响。

trait Readable:
  def read: String
trait Writable:
  def write(s: String): Unit

object Main:
  def use(r: Readable & Writable): Unit =
    r.write("x")
    println(r.read)

  def main(args: Array[String]): Unit =
    val rw = new Readable with Writable:
      def read = "data"
      def write(s: String) = println(s"wrote $s")
    use(rw)

组合多个特征

您可以对两个以上的类型求交集。结果必须具备列出的全部能力。

trait A: def a: Int
trait B: def b: Int
trait C: def c: Int

object Main:
  def sum(x: A & B & C): Int = x.a + x.b + x.c

  def main(args: Array[String]): Unit =
    val v = new A with B with C:
      def a = 1
      def b = 2
      def c = 3
    println(sum(v))

联合类型的类型别名

较长的联合类型可以通过 type 别名命名,从而保持签名的可读性。

type Json = Int | String | Boolean

object Main:
  def render(j: Json): String = j match
    case i: Int     => i.toString
    case s: String  => s"\"$s\""
    case b: Boolean => b.toString

  def main(args: Array[String]): Unit =
    println(render("hi"))
    println(render(true))

对联合类型进行穷尽匹配

编译器知道联合类型包含哪些成员,因此可以检查匹配是否完整。处理每个可能的分支,就能使匹配覆盖所有情况。

type Shape = "circle" | "square"

object Main:
  def sides(s: Shape): Int = s match
    case "circle" => 0
    case "square" => 4

  def main(args: Array[String]): Unit =
    println(sides("square"))

联合类型的拓宽

将联合类型的值赋给类型推断变量时,Scala 会保留联合类型。请注意,某些操作会将类型拓宽为最小上界;如果希望保留联合类型,请添加类型注解。

object Main:
  def pick(flag: Boolean): Int | String =
    if flag then 1 else "one"

  def main(args: Array[String]): Unit =
    val x: Int | String = pick(false)
    println(x)

何时使用哪一种

请选择合适的工具。

  • 联合类型:一个值属于多个类型中的一种(OR)。
  • 交集类型:一个值同时满足多个特征(AND)。
  • 联合类型避免使用包装类型;交集类型避免构建深层继承结构。
trait Logger: def log(s: String): Unit
trait Clock:  def now: Long

object Main:
  def run(env: Logger & Clock): Unit =
    env.log(s"time=${env.now}")

  def main(args: Array[String]): Unit =
    val env = new Logger with Clock:
      def log(s: String) = println(s)
      def now = 100L
    run(env)

快速检查

测试您对联合类型和交集类型的理解。

回顾

您学习了 Scala 3 的联合类型和交集类型。

  • A | B:值是 A 或 B,不需要包装器或公共父类型。
  • A & B:值同时是 A 和 B,并且满足交换律。
  • 使用模式匹配缩小联合类型;使用交集类型组合特征。
  • 使用 type 别名为较长的联合类型命名。
object Main:
  def length(x: String | List[Int]): Int = x match
    case s: String    => s.length
    case l: List[Int] => l.length

  def main(args: Array[String]): Unit =
    println(length("hello"))
    println(length(List(1, 2, 3)))

常见问题解答

「联合类型与交集类型」课时是免费的吗?

是的 — 「联合类型与交集类型」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 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 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 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