隱式轉換 - 更多詳細資訊
實作
從類型 S
到類型 T
的隱式轉換或檢視定義如下
- 類型為
S => T
或(=> S) => T
的implicit 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
值。
檢視會在三種情況下套用
- 如果表達式
e
是類型T
,而T
不符合表達式的預期類型pt
。在此情況下,會搜尋適用於e
且其結果類型符合pt
的隱式v
。搜尋過程與隱式參數相同,其中隱式範圍是T => pt
的範圍。如果找到這樣的檢視,則表達式e
會轉換為v(e)
。 - 在類型為
T
的選擇e.m
中,如果選擇器m
沒有表示T
的可存取成員。在此情況下,會搜尋適用於e
且其結果包含名為m
的可存取成員的檢視v
。搜尋過程與隱式參數相同,其中隱式範圍是T
的範圍。如果找到這樣的檢視,則選擇e.m
會轉換為v(e).m
。 - 在類型為
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
作為從 Int
到 String
的隱含轉換,而 Scala 3 會報告類型錯誤,因為 Map
不是 Conversion
的實例。
遷移路徑
用作檢視的隱含值應將其類型變更為 Conversion
。
對於受隱含解析變更影響的隱含轉換遷移,請參閱 隱含解析變更 以取得更多資訊。
參考
在本文中