隐式转换 - 更多细节
实现
从类型 S
到类型 T
的隐式转换或视图由以下任一定义
- 一个
implicit def
,其类型为S => T
或(=> S) => T
- 一个隐式值,其类型为
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
。
有关受隐式解析更改影响的隐式转换的迁移,请参阅隐式解析中的更改以获取更多信息。
参考
本文档