本篇内容摘自的第21章。 # implicit的使用都在以下几处: ## 1. 类型之间的相互转换

implicit def int2Fraction(n: Int) = Fraction(n, 1)
val result = 3 * Fraction(4, 5) // Calls int2Fraction(3

可以用于扩展已有类:

val contents = new File("README").read
class RichFile(val from: File) {
  def read = Source.fromFile(from.getPath).mkString
}
implicit def file2RichFile(from: File) = new RichFile(from)

2. 作为隐式参数,传给函数

case class Delimiters(left: String, right: String)

def quote(what: String)(implicit delims: Delimiters) =
  delims.left + what + delims.right

quote的调用(使用显式参数):

quote("Bonjour le monde")(Delimiters("«", "»"))

若要使用如下形式的隐式调用,则需要提供一个隐式转换,示例如下:

import FrenchPunctuation.quoteDelimiters
// or import FrenchPunctuation._

quote("Bonjour le monde")
object FrenchPunctuation {
  implicit val quoteDelimiters = Delimiters("«", "»")
  ...
}

上例中可以看出,quote的隐式参数可以是多个参数,以下代码虽然可以执行,但最好不要给隐式参数使用相同的类型。

def quote(what: String)(implicit left: String, right: String) // No!
implicit def test2String = ","

3. 隐式参数的隐式转换

def smaller[T](a: T, b: T) = if (a < b) a else b

以上代码并不能编译通过,因为编译器不能确定T是不是支持<这个运算符。 一般的隐式转换:

def smaller[T](a: T, b: T)(implicit order: T => Ordered[T])
  = if (order(a) < b) a else b

更简洁的隐式转换:

def smaller[T](a: T, b: T)(implicit order: T => Ordered[T])
  = if (a < b) a else b

上下文边界

在类定义中,声明一个上下文边界T:M就表示在当前的scope中存在一个隐式值M[T],所以的当要使用这种形式的定义是,要确定有这样一个隐式值(若没有编译器会报错)。 示例:

class Pair[T : Ordering](val first: T, val second: T) {
  def smaller(implicit ord: Ordering[T]) =
    if (ord.compare(first, second) < 0) first else second
}

scala/src/library/scala/math/Ordering.scala 中如下代码保证了Ordering[Int]

trait IntOrdering extends Ordering[Int] {
    def compare(x: Int, y: Int) =
      if (x < y) -1
      else if (x == y) 0
      else 1
  }
implicit object Int extends IntOrdering

implicitly的使用可以可以拿到ordering,代码如下:

class Pair[T : Ordering](val first: T, val second: T) {
  def smaller =
    if (implicitly[Ordering[T]].compare(first, second) < 0) first else second
}

implicitly的源码在Predef.scala中:

def implicitly[T](implicit e: T) = e
  // For summoning implicit values from the nether world

让我们更进一步,将ordering的部分,使用运算符替代:

class Pair[T : Ordering](val first: T, val second: T) {
  def smaller = {
    import Ordered._  // 引入Ordering到Ordered的隐式转换
    if (first < second) first else second
  }
}

自定义一个implicit value

implicit object PointOrdering extends Ordering[Point] {
  def compare(a: Point, b: Point) = ...
}

有关implicit的一些tips

  1. 在REPL中,键入:implicits可以看到所有不是从Predef引入的implicit。若要看所有的implicit(包含Predef中的implicit),则可以使用:implicits -v
  2. 若想察看,编译器为什么没有使用你想的那个隐式转换,可以显式调用隐式转换的函数。例如:fraction2Double(3) * Fraction(4, 5),看是否会报错。
  3. 如果想察看某个scala代码中使用的隐式转换,可以使用scalac -Xprint:typer MyProg.scala看到隐式转换后的代码。