情境抽象 的重新設計帶來了一些不相容性。
不相容性 | Scala 2.13 | Scala 3 遷移重寫 | Scalafix 規則 | 執行時期不相容性 |
---|---|---|---|---|
隱式 def 的類型 | ✅ | |||
隱式檢視 | 可能 | |||
檢視界線 | 不建議使用 | |||
在 A 和 => A 上有不明確的轉換 |
隱式定義的類型
在 Scala 3 中,隱式定義的類型(val
或 def
)需要明確給出。它們不能再被推斷出來。
名為 ExplicitResultTypes 的 Scalafix 規則可以自動撰寫遺失的類型註解。
隱式檢視
Scala 3 不支援從隱式函式值進行隱式轉換,形式為 implicit val ev: A => B
。
以下程式碼片段現在在 Scala 3 中無效
trait Pretty {
val print: String
}
def pretty[A](a: A)(implicit ev: A => Pretty): String =
a.print // In Scala 3, Error: value print is not a member of A
Scala 3 遷移編譯 可以警告您這些情況,但它不會嘗試修復它。
請注意,這種不相容性可能會產生執行時期不相容性並中斷您的程式。事實上,編譯器可以從更廣泛的範圍中找到另一個隱式轉換,這最終會導致執行時期出現不需要的行為。
已解析的轉換取決於編譯器模式
-source:3.0-migration
:編譯器執行ev
轉換-source:3.0
:編譯器無法執行ev
轉換,但它可以執行anyPretty
,這是不可取的
在 Scala 3 中,一個簡單的解決方法是明確提供正確的轉換
def pretty[A](a: A)(implicit ev: A => Pretty): String =
- a.print
+ ev(a).print
檢視邊界
檢視邊界已被棄用很長一段時間,但它們仍受 Scala 2.13 支援。它們無法再用 Scala 3 編譯。
def foo[A <% Long](a: A): Long = a
在此範例中,在 Scala 3 中,我們會收到以下錯誤訊息
-- Error: src/main/scala/view-bound.scala:2:12
2 | def foo[A <% Long](a: A): Long = a
| ^
| view bounds `<%' are deprecated, use a context bound `:' instead
訊息建議使用內容邊界取代檢視邊界,但它會變更方法的簽章。保留二進位相容性可能較容易且安全。為此,必須宣告隱式轉換並明確呼叫它。
小心不要陷入上述在 隱式檢視 中所述的執行時期不相容性。
-def foo[A <% Long](a: A): Long = a
+def foo[A](a: A)(implicit ev: A => Long): Long = ev(a)
在 A
和 => A
上的不明確轉換
在 Scala 2.13 中,在 A
上的隱式轉換會優先於在 => A
上的隱式轉換。在 Scala 3 中不再是如此,並導致不明確轉換。
例如,在此範例中
implicit def boolFoo(bool: Boolean): Foo = ???
implicit def lazyBoolFoo(lazyBool: => Boolean): Foo = ???
true.foo()
Scala 2.13 編譯器會選擇 boolFoo
轉換,但 Scala 3 編譯器無法編譯。
-- Error: src/main/scala/ambiguous-conversion.scala:4:19
9 | true.foo()
| ^^^^
|Found: (true : Boolean)
|Required: ?{ foo: ? }
|Note that implicit extension methods cannot be applied because they are ambiguous;
|both method boolFoo in object Foo and method lazyBoolFoo in object Foo provide an extension method `foo` on (true : Boolean)
暫時解決方法是明確撰寫轉換。
implicit def boolFoo(bool: Boolean): Foo = ???
implicit def lazyBoolFoo(lazyBool: => Boolean): Foo = ???
-true.foo()
+boolFoo(true).foo()