如何解决在 Scala 中采用相同输入类型的模糊隐式转换方法?

How to resolve ambiguous implicit conversion method that takes same input type in Scala?

我在这里阅读了其他相同的 question,但是对于某些内置 class,这种情况太具体了。我想在这里问一个简单的案例,希望得到一般的答案。

所以我有这个代码:

object ScalaApp {    
    case class Point(x: Int, y: Int);

    case class Rational(n: Int, d: Int) {
        def \(i: Int): List[Int] = n :: d:: i :: Nil
    }

    case class MyObject(x: Int, y: Int, z: Int) {
        def \(i: Int): List[Int] = x :: y:: i :: Nil
    }

    implicit def pointToRational(p: Point): Rational = Rational(p.x + 1, p.y * 2)

    implicit def pointToMyObject(p: Point): MyObject = MyObject(p.x, p.y, p.x+p.y)

    def main(args: Array[String]) {
        val p = Point(5, 7)
        val result = p \ 6    // compile error here
    }
}

我们可以看到当p 应用于\ 方法然后触发隐式转换时发生错误。 Scala 编译器将尝试查找任何定义了 def \(i: Int) 方法的导入或本地 class。如果找到,那么它将尝试寻找任何采用 Point 类型和 return 具有 def \(i: Int) 方法签名的对象类型的隐式转换方法。

碰巧这里有两个class有def \方法:RationalMyObject,还有两个隐式方法:pointToRational pointToMyObject 这两个 classes.

但是由于RationalMyObject都定义了def \,因此出现了歧义编译错误,因为它无法决定应该采用哪个。

Error:(30, 22) type mismatch;
 found   : p.type (with underlying type ScalaApp.Point)
 required: ?{def \(x: ? >: Int(6)): ?}
Note that implicit conversions are not applicable because they are ambiguous:
 both method pointToRational in object ScalaApp of type (p: ScalaApp.Point)ScalaApp.Rational
 and method pointToObj in object ScalaApp of type (p: ScalaApp.Point)ScalaApp.MyObject
 are possible conversion functions from p.type to ?{def \(x: ? >: Int(6)): ?}
        val result = p \ 6
                     ^

所以我的问题是,有没有办法解决模棱两可的隐式错误,但仍然保留两个 def \ 方法或不删除其中一个隐式转换方法?

不是真的。有时您可能会滥用 implicit resolution order (例如,将其中一个隐式移动到伴生对象中,或者将伴生对象扩展的特征移动到伴生对象中,这样它只会在您的直接范围内寻找隐式后才会被查看)。但通常你应该只确保范围内只有一个合适的隐式。请记住,您始终可以使用 {} 创建内部范围并在该范围内导入隐式:

val result = {import someImplicit; p \ 6}

你可以做到:

scala> val result = (p: Rational) \ 6 
result: List[Int] = List(6, 14, 6)

scala> val result = (p: MyObject) \ 6 
result: List[Int] = List(5, 7, 6)

所以你可以把它放在你的代码中而不是:

val result = p \ 6    // compile error here

另一种方法是将隐式对象移动到单独的对象中:

 object Implicits {
   implicit def pointToRational(p: Point): Rational = Rational(p.x + 1, p.y * 2)

   implicit def pointToMyObject(p: Point): MyObject = MyObject(p.x, p.y, p.x+p.y)
 }

 def main(args: Array[String]) {
    val p = Point(5, 7)
    import Implicits.pointToMyObject
    val result = p \ 6 
 }