在 GitHub 上编辑此页面

模式绑定

在 Scala 2 中,val 定义和 for 表达式中的模式绑定类型比较宽松。在编译时,可能会失败的匹配仍然会被接受,但可能会影响程序的运行时行为。从 Scala 3.2 开始,类型检查规则将被收紧,以便在编译时报告警告。

模式定义中的绑定

val xs: List[Any] = List(1, 2, 3)
val (x: String) :: _ = xs   // error: pattern's type String is more specialized
                            // than the right-hand side expression's type Any

此代码在 Scala 3.2 中会发出编译时警告(在 -source future 设置下,早期的 Scala 3.x 版本也会发出警告),而在 Scala 2 中则会在运行时以 ClassCastException 失败。在 Scala 3.2 中,只有当模式是不可反驳的时,才允许模式绑定,也就是说,只有当右边的类型符合模式的类型时才允许。例如,以下代码是可行的

val pair = (1, true)
val (x, y) = pair

有时,即使模式是可反驳的,也希望对数据进行分解。例如,如果在某个时刻知道列表 elems 非空,则可能希望像这样分解它

val first :: rest = elems   // error

这在 Scala 2 中有效。事实上,它是 Scala 2 规则的典型用例。但在 Scala 3.2 中,它会发出警告。可以通过在右侧添加 @unchecked 注解来避免警告。

val first :: rest = elems: @unchecked   // OK

这将使编译器接受模式绑定。如果底层假设 elems 永远不会为空是错误的,则可能会在运行时出现错误。

for 表达式中的模式绑定

类似的更改适用于 for 表达式中的模式。例如

val elems: List[Any] = List((1, 2), "hello", (3, 4))
for (x, y) <- elems yield (y, x) // error: pattern's type (Any, Any) is more specialized
                                 // than the right-hand side expression's type Any

这段代码在 Scala 3.2 中会发出编译时警告,而在 Scala 2 中,列表 elems 会被过滤以仅保留与模式 (x, y) 匹配的元组类型元素。在 Scala 3 中,可以通过在模式前添加 case 来获得过滤功能。

for case (x, y) <- elems yield (y, x)  // returns List((2, 1), (4, 3))

语法更改

for 表达式中的生成器可以以 case 为前缀。

Generator      ::=  [‘case’] Pattern1 ‘<-’ Expr

迁移

Scala 3.0 支持新语法。但是,为了实现 Scala 2 和 Scala 3 之间的平滑交叉编译,更改后的行为和额外的类型检查仅在 -source future 设置下启用。它们将在语言的 3.2 版本中默认启用。