1. 概述

在本教程中,我们将学习如何在 Scala 中表示 类型并集(Type Disjunction),也称为 联合类型(Union Types)

Scala 2 中没有内置的联合类型支持,但可以通过一些语言特性(如 Eithertypeclass)来实现类似功能。
⚠️ Scala 3 已原生支持联合类型,语法更加简洁直观。

我们将通过具体示例,逐一介绍这些实现方式。


2. 联合类型简介

顾名思义,联合类型表示多个类型的“并集”
比如,我们可以定义一个接受 IntString 类型参数的函数,这就是联合类型的典型应用场景。

Scala 是一门 强类型、静态类型语言,所有类型必须在编译期确定,这增强了类型安全性,但也限制了函数参数的灵活性。
比如,一个接受 Int 的函数不能直接传入 String

传统做法是通过 函数重载 来处理多种类型,但当类型数量增多时,重载会变得难以维护。
✅ 联合类型正是解决此类问题的利器。


3. 使用 Either 实现联合类型

Scala 标准库提供了 Either[A, B] 类型,表示两个类型的“不交并集”。
通常用于表示成功(Right)或失败(Left)两种情况。
⚠️ 自 Scala 2.12 起,Either右偏向(right-biased),可作为 Monad 使用。

我们可以通过 Either 定义两个类型的联合类型,例如:

def isIntOrString(t: Either[Int, String]): String = {
  t match {
    case Left(i)  => "%d is an Integer".format(i)
    case Right(s) => "%s is a String".format(s)
  }
}

println(isIntOrString(Left(10)))       // 输出: 10 is an Integer
println(isIntOrString(Right("hello"))) // 输出: hello is a String

❌ 但 Either 的缺点也很明显:

  • 只能表示两种类型的联合
  • 需要将值包装成 LeftRight,属于“装箱”类型

4. 任意数量的联合类型(Arbitrary-arity)

如果需要支持三种或更多类型的联合,嵌套 Either 会变得非常复杂。
✅ 更优雅的方式是使用 typeclass + implicit 机制。

4.1 定义三类型的联合类型

我们定义一个 sealed trait 来表示 IntStringBoolean 的联合类型:

sealed trait IntOrStringOrBool[T]

object IntOrStringOrBool {
  implicit val intInstance: IntOrStringOrBool[Int] = new IntOrStringOrBool[Int] {}
  implicit val strInstance: IntOrStringOrBool[String] = new IntOrStringOrBool[String] {}
  implicit val boolInstance: IntOrStringOrBool[Boolean] = new IntOrStringOrBool[Boolean] {}
}

4.2 编写函数使用该联合类型

def isIntOrStringOrBool[T: IntOrStringOrBool](t: T): String = t match {
  case i: Int    => "%d is an Integer".format(i)
  case s: String => "%s is a String".format(s)
  case b: Boolean => "%b is a Boolean".format(b)
}

调用示例:

println(isIntOrStringOrBool(10))     // 输出: 10 is an Integer
println(isIntOrStringOrBool("hello"))// 输出: hello is a String
println(isIntOrStringOrBool(true))   // 输出: true is a Boolean

✅ 优点:

  • 可扩展性强,只需添加新的 implicit 实例
  • 无需装箱,性能更好

4.3 泛型抽象优化

我们还可以进一步抽象为泛型形式:

sealed trait AOrB[A, B]

object AOrB {
  implicit def aInstance[A, B](a: A) = new AOrB[A, B] {}
  implicit def bInstance[A, B](b: B) = new AOrB[A, B] {}
}

def isIntOrString[T <% String AOrB Int](t: T): String = t match {
  case i: Int    => "%d is an Integer".format(i)
  case s: String => "%s is a String".format(s)
}

5. Scala 3 中的联合类型(Union Types)

在 Scala 3(即 Dotty)中,引入了 原生联合类型支持,使用 | 操作符表示:

def isIntOrString(t: Int | String | Boolean) = t match {
  case i: Int    => "%d is an Integer".format(i)
  case s: String => "%s is a String".format(s)
  case b: Boolean => "%b is a Boolean".format(b)
}

println(isIntOrString(10))     // 输出: 10 is an Integer
println(isIntOrString("hello"))// 输出: hello is a String
println(isIntOrString(true))   // 输出: true is a Boolean

✅ 特性总结:

  • 无需装箱(unboxed)
  • 支持任意数量类型(arbitrary arity)
  • 语法简洁,类型安全

6. 总结

在本教程中,我们介绍了以下几种实现联合类型的方式:

方法 适用版本 优点 缺点
Either Scala 2 标准库支持,语义清晰 仅支持两种类型,需装箱
Typeclass Scala 2 可扩展性强,支持多类型 实现略复杂
Union Types Scala 3 原生支持,语法简洁高效 仅适用于 Scala 3+

📌 推荐在 Scala 3 中优先使用 | 操作符定义联合类型,在 Scala 2 中则根据场景选择 Eithertypeclass 方案。


📘 示例代码已上传至 GitHub:Baeldung/scala-tutorials


原始标题:Type Disjunction (Union Types) in Scala