此文档页面特定于 Scala 3,可能涵盖 Scala 2 中不可用的新概念。除非另有说明,本页中的所有代码示例均假定您使用的是 Scala 3。
用于类型时,|
运算符会创建一个所谓的联合类型。类型 A | B
表示既是类型 A
又是类型 B
的值。
在以下示例中,help
方法接受一个名为 id
的联合类型 Username | Password
的参数,它可以是 Username
或 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
方法。
此代码是一个灵活且类型安全的解决方案。如果你尝试传递 Username
或 Password
以外的类型,编译器会将其标记为错误
help("hi") // error: Found: ("hi" : String)
// Required: Username | Password
如果你尝试向 match
表达式添加一个不匹配 Username
或 Password
类型的 case
,你也会收到一个错误
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
等标记特征来混淆类型层次结构也会使代码更难阅读。
标记联合
另一种选择是定义一个单独的枚举类型,如下所示
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 then name else password
val a: Object = Username(Eve)
scala> val b: Password | Username = if true then name else password
val b: Password | Username = Username(Eve)
a
的类型是 Object
,它是 Username
和 Password
的超类型,但不是最小超类型 Password | Username
。如果你想要最小超类型,则必须明确给出它,就像对 b
所做的那样。
联合类型是交集类型的对偶。与交集类型中的
&
一样,|
也具有交换律:A | B
与B | A
是同一种类型。
此页面的贡献者
内容
- 简介
- Scala 特性
- 为什么选择 Scala 3?
- Scala 体验
- Hello, World!
- REPL
- 变量和数据类型
- 控制结构
- 领域建模
- 方法
- 一类函数
- 单例对象
- 集合
- 上下文抽象
- 顶级定义
- 总结
- 初识类型
- 字符串插值
- 控制结构
- 领域建模
- 工具
- OOP 建模
- FP 建模
- 方法
- 方法特性
- Scala 3 中的主方法
- 总结
- 函数
- 匿名函数
- 函数变量
- Eta 展开
- 高阶函数
- 编写自己的 map 方法
- 创建返回函数的方法
- 总结
- 打包和导入
- Scala 集合
- 集合类型
- 集合方法
- 总结
- 函数式编程
- 什么是函数式编程?
- 不可变值
- 纯函数
- 函数即值
- 函数式错误处理
- 总结
- 类型和类型系统
- 推断类型
- 泛型
- 交集类型
- 联合类型
- 代数数据类型
- 协变
- 不透明类型
- 结构类型
- 依赖函数类型
- 其他类型
- 上下文抽象
- 扩展方法
- 上下文参数
- 上下文边界
- 给定导入
- 类型类
- 多重相等
- 隐式转换
- 总结
- 并发
- Scala 工具
- 使用 sbt 构建和测试 Scala 项目
- 工作表
- 与 Java 交互
- 面向 Java 开发人员的 Scala
- 面向 JavaScript 开发人员的 Scala
- 面向 Python 开发人员的 Scala
- 下一步