Scala 2.13 类型检查器在某些特定情况下是不健全的。这可能导致我们在意想不到的地方遇到令人惊讶的运行时错误。Scala 3 基于更强大的理论基础,因此类型检查器中的这些不健全错误现在已得到修复。
方差检查中的不健全修复
在 Scala 2 中,默认参数和内部类不受方差检查的影响。这是不健全的,并且可能导致运行时失败,如 Scala 3 存储库中的 此测试 所示。
Scala 3 编译器不再允许这样做。
class Foo[-A](x: List[A]) {
def f[B](y: List[B] = x): Unit = ???
}
class Outer[+A](x: A) {
class Inner(y: A)
}
因此,如果你在 Scala 3 中编译,你将收到以下错误。
此类每个问题都需要特别注意。你可以逐案尝试以下选项
- 使类型
A
不变 - 在类型参数
B
上添加下界或上界 - 添加一个新的方法重载
在我们的示例中,我们可以选择这两种解决方案
或者,作为临时解决方案,你还可以使用 uncheckedVariance
注解
模式匹配中的不健全修复
Scala 3 修复了模式匹配中的一些不健全错误,防止一些语义错误的匹配表达式进行类型检查。
例如,combineReq
中的匹配表达式可以使用 Scala 2.13 编译,但不能使用 Scala 3 编译。
trait Request
case class Fetch[A](ids: Set[A]) extends Request
object Request {
def combineFetch[A](x: Fetch[A], y: Fetch[A]): Fetch[A] = Fetch(x.ids ++ y.ids)
def combineReq(x: Request, y: Request): Request = {
(x, y) match {
case (x @ Fetch(_), y @ Fetch(_)) => combineFetch(x, y)
}
}
}
在 Scala 3 中,错误消息是
正确的做法是,没有证据表明 x
和 y
具有相同的类型参数 A
。
从 Scala 2 开始,这显然是一种改进,可帮助我们找到代码中的错误。为了解决这种不兼容性,最好找到一种可以由编译器检查的解决方案。这并不总是一件容易的事,有时甚至是不可能的,在这种情况下,代码很可能在运行时失败。
在此示例中,我们可以通过声明 A
是两个类型参数的共同祖先来放宽对 x
和 y
的约束。这使得编译器成功地对代码进行类型检查。
def combineFetch[A](x: Fetch[_ <: A], y: Fetch[_ <: A]): Fetch[A] = Fetch(x.ids ++ y.ids)
或者,一种通用但又不安全的解决方案是强制转换。