隐式参数(IMPLICIT PARAMETERS)
方法可以有一个隐式参数列表,在参数列表的开头用 implicit 关键字标记。如果参数列表中的参数没有像往常那样传递,Scala 将会查看它是否可以获取一个正确类型的隐式值,如果可以,则自动的传递它。
Scala 将寻找参数的位置分为两类:
- 在带有隐式参数块的方法被调用时,Scala 首先会查找可以被直接获取(没有前缀)的隐式定义和隐式参数。
- 然后它会查看与隐式候选类型相关联的全部伴生对象(companion objects)中的隐式标记成员。
可以在 FAQ 找到有关 Scala 是如何寻找隐式位置的更详细指南。
在下面的例子中,我们定义了一个方法 sum,它使用 monoid 的add 和 unit 计算元素列表的总和。请注意隐式值不能是顶级的(top-level)。
abstract class Monoid[A] {
def add(x: A, y: A): A
def unit: A
}
object ImplicitTest {
implicit val stringMonoid: Monoid[String] = new Monoid[String] {
def add(x: String, y: String): String = x concat y
def unit: String = ""
}
implicit val intMonoid: Monoid[Int] = new Monoid[Int] {
def add(x: Int, y: Int): Int = x + y
def unit: Int = 0
}
def sum[A](xs: List[A])(implicit m: Monoid[A]): A =
if (xs.isEmpty) m.unit
else m.add(xs.head, sum(xs.tail))
def main(args: Array[String]): Unit = {
println(sum(List(1, 2, 3))) // uses IntMonoid implicitly
println(sum(List("a", "b", "c"))) // uses StringMonoid implicitly
}
}
Nonoid 定义了一个 add 操作,它组合了一对 A 并返回另一个 A,以及一个称为 unit 的操作能够创建一些特定的 A。
为了演示隐式参数是如何工作的,我们首先分别为字符串和整数定义了 monoid StringMonoid 和 IntMonoid。
关键字 implicit 指明相应的对象可以被隐式地使用。
方法 sum 接受 List[A] 并返回 A,它将从 unit 中取出初始值 A,并将列表中每下一个 A 与方法 add 结合起来 。使参数 m 隐式在这里指在我们在调用方法时只需提供 xs 参数,如果 Scala 可以找到隐式 Monoid[A] 用于隐式参数 m 的话。
在我们的 main 方法中,我们调用两次 sum 方法,并只提供了 xs 参数。现在 Scala 将会在上面提到的范围内查找隐式。第一次调用 sum 方法时传递了一个 List[Int] 给 xs,这意味着 A 是 Int 类型。隐式参数列表 m 被忽略了,因此 Scala 将会查找隐式类型 Monoid[Int]。按照第一查找规则读取:
在带有隐式参数块的方法被调用时,Scala 首先会查找可以被直接获取(没有前缀)的隐式定义和隐式参数。
intMonoid 是一个可以在 main 函数中被直接获取的隐式定义。它也是正确的类型,所以它会自动地传递给 sum 方法。
第二次调用 sum 时 传入了一个 List[String],这意味着 A 是 String 类型。隐式查找的方式与 Int 相同,但是这次将会找到 stringMonoid 方法并自动地将其传入为 m。
该程序输出结果为:
6
abc

隐式转换(IMPLICIT CONVERSIONS)
一个 S 类型到 T 类型的隐式转换由具有函数类型 S => T 隐式值或由可转化为该类型的隐式方法定义。
隐式转换适用于两种场景:
- 如果表达式
e是类型S,并且S不符合表达式的预期类型T。 - 在选择带有
S类型e的e.m中,如果选择器m不表示成员S。
在第一种情况下,搜索一个适用于 e 的转换 c 并且其结果要符合类型 T。第二种情况,搜索一个适用于 e 的转换 c 并且其结果类型要包含名为 m 的成员。
如果一个隐式方法 List[A] => Ordered[List[A]] 在范围内,隐式方法 Int => Ordered[Int] 也在,那接下来对两个类型列表 List[Int] 的操作是合法的:
List(1,2,3) <= List(4,5)
通过 scala.Predef.intWrapper,隐式方法 Int => Ordered[Int] 自动地被提供。下面提供一个隐式方法 List[A] => Ordered[List[A]] 实现的例子。
import scala.language.implicitConversions
implicit def list2ordered[A](x: List[A])
(implicit elem2ordered: A => Ordered[A]): Ordered[List[A]] =
new Ordered[List[A]] {
//replace with a more useful implementation
def compare(that: List[A]): Int = 1
}
隐式导入的对象 scala.Predef 声明了几个预定义类型(如 Pair)和方法(如 assert),而且也包含几个隐式转换。
例如,当调用一个 Java 方法期望获得一个 java.lang.Integer 时,你可以使用 scala.Int 代替。这是因为预定义包括了接下来的隐式转换:
import scala.language.implicitConversions
implicit def int2Integer(x: Int) =
java.lang.Integer.valueOf(x)
因为隐式转换有缺陷,如果不加以区分的使用,编译器将会在编译隐式转换定义时发出警告。
可以采取下面一些方法来关闭警告:
- 导入
scala.language.implicitConversions到隐式转换定义的范围 - 调用编译器时带参数
-language:implicitConversions
这样,当编译器在进行转换时,就不会再发出警告。

本文介绍了Scala中的隐式参数和隐式转换的概念及其工作原理。通过实例展示了如何定义和使用隐式参数来简化方法调用,以及如何利用隐式转换解决类型不匹配的问题。
77

被折叠的 条评论
为什么被折叠?



