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
,可以是 Useername
或 Password
:
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
方法。
此代码是一种灵活且类型安全的解决方案。
如果您尝试传入Useername
或 Password
以外的类型,编译器会将其标记为错误:
help("hi") // error: Found: ("hi" : String)
// Required: Username | Password
如果您尝试将 case
添加到与 Username
或 Password
类型不匹配的 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
表示 Username
和 Password
的 标记_联合。
但是,这种联合建模方式需要_显式包装和展开,例如,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
,它是 Username
和 Password
的超类型,但不是二者最小的超类型 Password | Username
。
如果你想要最小的超类型,你必须明确地给出它,就像对 b
所做的那样。
联合类型是交集类型的对偶。 和具有交集类型的
&
一样,|
也是可交换的:A | B
与B | A
是同一类型。
Contributors to this page:
Contents
- 导言
- Scala 3 特性
- 为什么是 Scala 3 ?
- Scala 的味道
- Hello, World!
- The REPL
- 变量和数据类型
- 控制结构
- 领域建模
- 方法
- 头等函数
- 单例对象
- 集合
- 上下文抽象
- 顶层定义
- 总结
- 类型初探
- 字符串插值
- 控制结构
- 领域建模
- 工具
- OOP 领域建模
- 函数式领域建模
- 方法
- 方法特性
- main 方法
- 总结
- 函数
- 匿名函数
- 函数变量
- Eta 扩展
- 高阶函数
- 自定义 map 函数
- 创建可以返回函数的方法
- 总结
- 打包和导入
- Scala 集合
- 集合类型
- 集合方法
- 总结
- 函数式编程
- 什么是函数式编程?
- 不可变值
- 纯函数
- 函数是值
- 函数式错误处理
- 总结
- 类型和类型系统
- 类型推断
- 泛型
- 相交类型
- 联合类型
- 代数数据类型
- 型变
- 不透明类型
- 结构化类型
- 依赖函数类型
- 其他类型
- 上下文抽象
- 扩展方法
- Given 实例和 Using 语句
- 上下文绑定
- Given 导入
- 实现类型类
- 多元相等性
- 隐式转换
- 总结
- 并发
- Scala 工具
- 使用 sbt 构建和测试 Scala 项目
- worksheet
- 与 Java 交互
- 向 Java 开发者介绍Scala
- Scala for JavaScript Developers
- Scala for Python Developers
- 下一步去哪