Link Search Menu Expand Document

模式绑定

Scala 2 中,val 定义和 for 表达式中的模式绑定是宽松类型的。 在编译时会接受可能失败的匹配,但可能影响程序的运行时行为。 从 Scala 3.1 开始,类型检查规则将会更加严格,会在编译时报出警告。

模式定义中的绑定

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.1(以及 Scala 3.0 中的 -source future 选项下)会给出编译时警告, 而 Scala 2 中它会在运行时失败并抛出 ClassCastException。 Scala 3.1 中,只有模式是不会失败(irrefutable)的(右侧表达式的类型符合模式的类型)情况下才允许使用模式绑定。 例如,这样可以的:

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

有时用户可能想无论如何都要解构数据,即使模式可能失败。 例如,在知道列表 elems 非空时,想要这样解构它:

val first :: rest = elems   // error

这在 Scala 2 中有效。事实上这是 Scala 2 规则的典型用例。 但在 Scala 3.1 中这会给出一个警告。可以在右侧使用 @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.1 中会给出一个编译时警告, 而在 Scala 2 中列表 elems 的元素会被过滤, 只保留能够与模式 (x, y) 匹配的元组类型的元素。 在 Scala 3 中可以加上 case 前缀获得过滤的功能:

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

语法变化

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

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

迁移

Scala 3.0 支持这种新语法。但是,为了在 Scala 2 与 Scala 3 之间实现平滑的交叉编译, 只有在 -source future 选项下才会启用变化后的行为和附加的类型检查。 Scala 3.1 中它们会被默认启用。