Scala 3 迁移指南

类型推断

语言

本页描述的两个不兼容性是类型推断规则的有意更改。

其他不兼容性可能是由类型推断算法的替换引起的。新算法比旧算法更好,但有时它可能会在 Scala 2.13 成功的情况下失败

始终建议明确编写所有公共值和方法的结果类型。它可以防止库的公共 API 由于不同的推断类型而随着 Scala 版本而更改。

在 Scala 3 迁移之前,可以通过在 Scalafix 中使用 ExplicitResultTypes 规则来完成此操作。

覆盖方法的返回类型

在 Scala 3 中,覆盖方法的返回类型是通过从基方法继承推断出来的,而在 Scala 2.13 中,它是从覆盖方法的左侧推断出来的。

class Parent {
  def foo: Foo = new Foo
}

class Child extends Parent {
  override def foo = new RichFoo(super.foo)
}

在此示例中,Child#foo 在 Scala 2.13 中返回 RichFoo,但在 Scala 3 中返回 Foo。这会导致编译器错误,如下所示。

class Foo

class RichFoo(foo: Foo) extends Foo {
  def show: String = ""
}

class Parent {
  def foo: Foo = new Foo
}

class Child extends Parent {
  override def foo = new RichFoo(super.foo)
}

(new Child).foo.show // Scala 3 error: value show is not a member of Foo

在涉及隐式转换和运行时转换的一些罕见情况下,它甚至可能导致运行时失败。

解决方案是显式指定覆盖方法的返回类型

class Child extends Parent {
-  override def foo = new RichFoo(super.foo)
+  override def foo: RichFoo = new RichFoo(super.foo)
}

反射类型

Scala 2 反射调用已被删除,并由更广泛的 程序化结构类型 取代。

Scala 3 可以通过在导入 scala.language.reflectiveCalls 的任何地方提供 scala.reflect.Selectable.reflectiveSelectable 来模仿 Scala 2 反射调用。但是,Scala 3 编译器默认情况下不会推断结构类型,因此无法编译

import scala.language.reflectiveCalls

val foo = new {
  def bar: Unit = ???
}

foo.bar // Error: value bar is not a member of Object

简单的解决方案是写下结构类型。

import scala.language.reflectiveCalls

- val foo = new {
+ val foo: { def bar: Unit } = new {
  def bar: Unit = ???
}

foo.bar

此页面的贡献者