如何解决在 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 \
方法:Rational
和MyObject
,还有两个隐式方法:pointToRational
pointToMyObject
这两个 classes.
但是由于Rational
和MyObject
都定义了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
}
我在这里阅读了其他相同的 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 \
方法:Rational
和MyObject
,还有两个隐式方法:pointToRational
pointToMyObject
这两个 classes.
但是由于Rational
和MyObject
都定义了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
}