1. 概述
在本教程中,我们将学习如何在 Scala 中表示 类型并集(Type Disjunction),也称为 联合类型(Union Types)。
✅ Scala 2 中没有内置的联合类型支持,但可以通过一些语言特性(如 Either
和 typeclass
)来实现类似功能。
⚠️ Scala 3 已原生支持联合类型,语法更加简洁直观。
我们将通过具体示例,逐一介绍这些实现方式。
2. 联合类型简介
顾名思义,联合类型表示多个类型的“并集”。
比如,我们可以定义一个接受 Int
或 String
类型参数的函数,这就是联合类型的典型应用场景。
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
的缺点也很明显:
- 只能表示两种类型的联合
- 需要将值包装成
Left
或Right
,属于“装箱”类型
4. 任意数量的联合类型(Arbitrary-arity)
如果需要支持三种或更多类型的联合,嵌套 Either
会变得非常复杂。
✅ 更优雅的方式是使用 typeclass + implicit 机制。
4.1 定义三类型的联合类型
我们定义一个 sealed trait
来表示 Int
、String
、Boolean
的联合类型:
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 中则根据场景选择 Either
或 typeclass
方案。
📘 示例代码已上传至 GitHub:Baeldung/scala-tutorials