在 GitHub 上編輯此頁面

隱式轉換 - 更多詳細資訊

實作

從類型 S 到類型 T 的隱式轉換或檢視定義如下

  • 類型為 S => T(=> S) => Timplicit def
  • 類型為 Conversion[S, T] 的隱式值

標準函式庫定義了一個抽象類別 Conversion

package scala
@java.lang.FunctionalInterface
abstract class Conversion[-T, +U] extends Function1[T, U]:
  def apply(x: T): U

函數文字會自動轉換為 Conversion 值。

檢視會在三種情況下套用

  1. 如果表達式 e 是類型 T,而 T 不符合表達式的預期類型 pt。在此情況下,會搜尋適用於 e 且其結果類型符合 pt 的隱式 v。搜尋過程與隱式參數相同,其中隱式範圍是 T => pt 的範圍。如果找到這樣的檢視,則表達式 e 會轉換為 v(e)
  2. 在類型為 T 的選擇 e.m 中,如果選擇器 m 沒有表示 T 的可存取成員。在此情況下,會搜尋適用於 e 且其結果包含名為 m 的可存取成員的檢視 v。搜尋過程與隱式參數相同,其中隱式範圍是 T 的範圍。如果找到這樣的檢視,則選擇 e.m 會轉換為 v(e).m
  3. 在類型為 T 的應用程式 e.m(args) 中,如果選擇器 m 表示 T 的一些可存取成員,但這些成員都不適用於參數 args。在此情況下,會搜尋適用於 e 且其結果包含適用於 args 的方法 m 的檢視 v。搜尋過程與隱式參數相同,其中隱式範圍是 T 的範圍。如果找到這樣的檢視,則應用程式 e.m(args) 會轉換為 v(e).m(args)

與 Scala 2 隱式轉換的差異

在 Scala 2 中,參數以值傳遞的檢視優先於參數以名稱傳遞的檢視。在 Scala 3 中不再是這樣。在 Scala 2 中會套用此規則的情況,會發出報告不明確轉換的類型錯誤

implicit def conv1(x: Int): String = x.toString
implicit def conv2(x: => Int): String = x.toString

val x: String = 0 // Compiles in Scala2 (uses `conv1`),
                  // type error in Scala 3 because of ambiguity.

在 Scala 2 中,函數類型的隱含值會被視為潛在的檢視。在 Scala 3 中,這些隱含值需要有類型 Conversion

// Scala 2:
def foo(x: Int)(implicit conv: Int => String): String = x

// Becomes with Scala 3:
def foo(x: Int)(implicit conv: Conversion[Int, String]): String = x

// Call site is unchanged:
foo(4)(_.toString)

// Scala 2:
implicit val myConverter: Int => String = _.toString

// Becomes with Scala 3:
implicit val myConverter: Conversion[Int, String] = _.toString

請注意,隱含轉換也受到 Scala 2 和 Scala 3 之間隱含解析變更 的影響。

變更動機

在 Scala 3 中引入 scala.Conversion,並決定將此類型的隱含值限制為潛在檢視,是出於希望消除語言中的意外行為

implicit val m: Map[Int, String] = Map(1 -> "abc")

val x: String = 1  // Scala 2: assigns "abc" to x
                   // Scala 3: type error

此片段包含類型錯誤。val x 的右側不符合類型 String。在 Scala 2 中,編譯器會使用 m 作為從 IntString 的隱含轉換,而 Scala 3 會報告類型錯誤,因為 Map 不是 Conversion 的實例。

遷移路徑

用作檢視的隱含值應將其類型變更為 Conversion

對於受隱含解析變更影響的隱含轉換遷移,請參閱 隱含解析變更 以取得更多資訊。

參考

如需有關隱含解析的更多資訊,請參閱 隱含解析變更。其他詳細資訊可在 PR #2065 中取得。