模式绑定
在 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 版本中默认启用。