Scala 3 — Book

联合类型

Language
This doc page is specific to Scala 3, and may cover new concepts not available in Scala 2. Unless otherwise stated, all the code examples in this page assume you are using Scala 3.

用于类型,| 操作符创建一个所谓的_联合类型_。 类型 A | B 表示要么是 A 类型的值,要么是 B 类型的值。

在下面的例子中,help 方法接受一个名为 id 的联合类型 Username | Password,可以是 UseernamePassword

case class Username(name: String)
case class Password(hash: Hash)

def help(id: Username | Password) =
  val user = id match
    case Username(name) => lookupName(name)
    case Password(hash) => lookupPassword(hash)
  // more code here ...

我们通过使用模式匹配区分二者,从而实现 help 方法。

此代码是一种灵活且类型安全的解决方案。 如果您尝试传入UseernamePassword 以外的类型,编译器会将其标记为错误:

help("hi")   // error: Found: ("hi" : String)
             //        Required: Username | Password

如果您尝试将 case 添加到与 UsernamePassword 类型不匹配的 match 表达式中,也会出现错误:

case 1.0 => ???   // ERROR: this line won’t compile

联合类型的替代方案

如图所示,联合类型可用于替代几种不同的类型,而不要求这些类型是定制类层次结构的一部分,也不需要显式包装。

预先规划类层次结构

其他语言需要预先规划类层次结构,如下例所示:

trait UsernameOrPassword
case class Username(name: String) extends UsernameOrPassword
case class Password(hash: Hash) extends UsernameOrPassword
def help(id: UsernameOrPassword) = ...

预先计划不能很好地扩展,例如,API 用户的需求可能无法预见。 此外,使用诸如 UsernameOrPassword 之类的标记 trait 使类型层次结构混乱也会使代码更难阅读。

标记联合

另一种选择是定义一个单独的枚举类型,如:

enum UsernameOrPassword:
  case IsUsername(u: Username)
  case IsPassword(p: Password)

枚举 UsernameOrPassword 表示 UsernamePassword标记_联合。 但是,这种联合建模方式需要_显式包装和展开,例如,Username 不是 UsernameOrPassword 的子类型。

联合类型推断

_仅当_明确给出这种类型时,编译器才会将联合类型分配给表达式。 例如,给定这些值:

val name = Username("Eve")     // name: Username = Username(Eve)
val password = Password(123)   // password: Password = Password(123)

这个 REPL 示例展示了在将变量绑定到 if/else 表达式的结果时如何使用联合类型:

scala> val a = if (true) name else password
val a: Object = Username(Eve)

scala> val b: Password | Username = if (true) name else password
val b: Password | Username = Username(Eve)

a 的类型是 Object,它是 UsernamePassword 的超类型,但不是二者最小的超类型 Password | Username。 如果你想要最小的超类型,你必须明确地给出它,就像对 b 所做的那样。

联合类型是交集类型的对偶。 和具有交集类型的 & 一样,| 也是可交换的:A | BB | A 是同一类型。

Contributors to this page: