Scala 专题教程-隐式变换和隐式参数(8):当有多个隐含转换可以选择时

jerry Scala 2015年11月25日 收藏

有时在当前作用域可能存在多个符合条件的隐含转换,在大多数情况下,Scala编译器在此种情况下拒绝自动插入转换代码。隐含转换只有在转换非常明显的情况下工作良好,编译器只要例行公事插入所需转换代码即可。如果当前作用域存在多个可选项,编译器不知道优先选择哪一个使用。

  1. scala> def printLength(seq:Seq[Int]) = println (seq.length)
  2. printLength: (seq: Seq[Int])Unit
  3.  
  4. scala> implicit def intToRange(i:Int) = 1 to i
  5. intToRange: (i: Int)scala.collection.immutable.Range.Inclusive
  6.  
  7. scala> implicit def intToDigits(i:Int) = i.toString.toList.map( _.toInt)
  8. intToDigits: (i: Int)List[Int]
  9.  
  10. scala> printLength(12)
  11. <console>:11: error: type mismatch;
  12. found : Int(12)
  13. required: Seq[Int]
  14. Note that implicit conversions are not applicable because they are ambiguous:
  15. both method intToRange of type (i: Int)scala.collection.immutable.Range.Inclusive
  16. and method intToDigits of type (i: Int)List[Int]
  17. are possible conversion functions from Int(12) to Seq[Int]
  18. printLength(12)
  19.  

这个例子产生的歧义是非常明显的,将一个整数转换成一组数字和转换成一个序列是明显两个不同的变化。此时应该明确指明使用那个变换:

  1. scala> intToDigits(12)
  2. res1: List[Int] = List(49, 50)
  3.  
  4. scala> printLength(intToDigits(12))
  5. 2
  6.  
  7. scala> printLength(intToRange(12))
  8. 12

在Scala2.7以前,Scala编译器碰到多个可选项时都这么处理,从2.8版本以后,这个规则不再这么严格,如果当前作用域内有多个可选项,Scala编译器优先选择类型更加明确的隐含转换。 比如两个隐含变换一个参数类型为String,而另外一个类型为Any。两个隐含转换都可以备选项时,Scala编译器优先选择参数类型为String的那个隐含转换。

“更明确”的一个判断规则如下:

  • 参数的类型是另外一个类型的子类型
  • 如果两个转换都是对象的方法,前对象是派生于另外一个对象。