# Scala 隐式转换和隐式参数

### Implict conversion

`scala.Predef``scala.Int`转换为`java.lang.Integer`的隐式转换

``````implicit def int2Integer(x: Int) = java.lang.Integer.valueOf(x)
``````

• 标记：必须用`implicit`标记
``````implicit def intToString(x: Int) = x.toString
``````
• 作用域：只使用作用域内部的隐式转换，转换方法作为单一标识符，或者是位于伴生对象

• 无歧义：只能存在一种隐式转换，不能冲突
• 显式优先：如果类型检查无误，不会尝试进行隐式操作

#### 用途

• 隐式转换为期望类型，例如`Int``Double`
``````implicit def int2double(x: Int): Double = x.toDouble
``````
• 转换方法调用对象的类型

``````class Rational(n: Int, d: Int) {
...
def + (that: Rational): Rational = ...
def + (that: Int): Rational = ...
}

scala> val oneHalf = new Rational(1, 2)
oneHalf: Rational = 1/2
scala> implicit def intToRational(x: Int) = new Rational(x, 1)
intToRational: (x: Int)Rational
scala> 1 + oneHalf
res2: Rational = 3/2
``````
• 模拟新的语法
`Map(1 -> "one", 2 -> "two", 3 -> "three")`中的箭头语法定义，位于`Predef.scala`
``````  implicit final class ArrowAssoc[A](private val self: A) extends AnyVal {
@inline def -> [B](y: B): Tuple2[A, B] = Tuple2(self, y)
def →[B](y: B): Tuple2[A, B] = ->(y)
}
``````
• 隐式类
编译器自动生成隐式转换，从隐式类的参数转换为对应的类本身
隐式类不能是一个样本类，它的构造函数只能有一个参数，同时必须位于其他对象，类或特质中
``````case class Rectangle(width: Int, height: Int)
//定义隐式类
implicit class RectangleMaker(width: Int) {
def x(height: Int) = Rectangle(width, height)
}
// 编译器自动生成一个隐式方法，从构造函数转换为隐式类
implicit def RectangleMaker(width: Int) = new RectangleMaker(width)
//使用
scala> val myRectangle = 3 x 4
myRectangle: Rectangle = Rectangle(3,4)
``````
• 隐式对象
形如`implicit object`，例如`scala.math.Ordering`的伴生对象中定义了众多的隐式对象
``````  trait IntOrdering extends Ordering[Int] {
def compare(x: Int, y: Int) = java.lang.Integer.compare(x, y)
}
implicit object Int extends IntOrdering
``````

### Implicit parameter

``````  def maxListImpParm[T](elements: List[T])
(implicit ordering: Ordering[T]): T =
elements match {
case List() =>
throw new IllegalArgumentException("empty list!")
case List(x) => x
case x :: rest =>
val maxRest = maxListImpParm(rest)(ordering)//ordering可以省略，编译器可以用方法的隐式参数填充
if (ordering.gt(x, maxRest)) x
else maxRest
}
``````

``````@inline def implicitly[T](implicit e: T) = e
``````

``````  def maxList[T](elements: List[T])
(implicit ordering: Ordering[T]): T =
elements match {
case List() =>
throw new IllegalArgumentException("empty list!")
case List(x) => x
case x :: rest =>
val maxRest = maxList(rest)
if (implicitly[Ordering[T]].gt(x, maxRest)) x
else maxRest
}
``````

#### 视界 Context Bound

``````  def maxList[T : Ordering](elements: List[T]): T =
elements match {
case List() =>
throw new IllegalArgumentException("empty list!")
case List(x) => x
case x :: rest =>
val maxRest = maxList(rest)
if (implicitly[Ordering[T]].gt(x, maxRest)) x
else maxRest
}
``````

``````def g[A : B](a: A) = h(a)
//等价于
def g[A](a: A)(implicit ev: B[A]) = h(a) //h需要一个隐式类型为B[A]的隐式值
``````

Scala数组是泛型，会进行类型擦除，为了在运行时获取类型信息，需要`ClassTag`

``````scala> def mkArray[T : ClassTag](elems: T*) = Array[T](elems: _*)
mkArray: [T](elems: T*)(implicit evidence\$1: scala.reflect.ClassTag[T])Array[T]

scala> mkArray(42, 13)
res0: Array[Int] = Array(42, 13)

scala> mkArray("Japan","Brazil","Germany")
res1: Array[String] = Array(Japan, Brazil, Germany)
//内部实际调用
mkArray[String]("Japan", "Brazil", "Germany")((ClassTag.apply[String](classOf[java.lang.String]): scala.reflect.ClassTag[String]));
``````

`scala.reflect.ClassTag`通过`runtimeClass`字段获取运行时类型信息

#### 多个隐式转换的选择

• 如果前者的参数类型是后者的子类型，选前者，例如字符串优先选`String`而不是`Any`类型的参数
• 位于子类中的方法优先于父类中的方法，例如遇到`"abc".reverse`方法时，优先使用的是转换为`StringOps`的隐式转换
``````//Predet中的方法
@inline implicit def augmentString(x: String): StringOps = new StringOps(x)
//Predef继承的LowPriorityImplicits类中的隐式转换
implicit def wrapString(s: String): WrappedString = if (s ne null) new WrappedString(s) else null
``````