当范围内存在多个不明确的隐式时,Scala 编译器如何选择隐式
How implicit is chosen by Scala compile when we have multiple ambiguous implicit present in scope
我是 Scala 的新手,我正在参考书 “Scala for Impatient - 第二版”。
我正在编写一个小代码,我在其中创建了一个 class Fraction
,它作为两个 Int 字段 num
(数字)和 den
(分母)。 class 有一个方法 *
执行 num
和 den
和 returns 新 Fraction
实例的乘法。
在这里,为了理解 implicit
的工作原理,我创建了一个对象 FractionConversions
,它有助于两个隐式方法 intToFraction
和 fractionToDouble
.
在测试 ImplicitConversionTester
中的代码时,我导入了 FractionConversions._
路径,因此编译器可以使用这两个隐式方法。
现在,参考代码,让图片更清晰。
package OperatorOverloadingAndImplicitConversion
class Fraction(n : Int, d : Int) {
private val num = this.n
private val den = this.d
def *(other : Fraction) = new Fraction(num*other.num, den*other.den)
override def toString: String = {
s"Value of Fraction is : ${(num*0.1)/den}"
}
}
object Fraction {
def apply(n: Int, d: Int): Fraction = new Fraction(n, d)
}
object FractionConversions {
implicit def fractionToDouble(f : Fraction): Double = {
println("##### FractionConversions.fractionToDouble called ...")
(f.num*0.1)/f.den
}
implicit def intToFraction(n : Int) : Fraction = {
println("##### FractionConversions.intToFraction called ...")
new Fraction(n,1)
}
}
object ImplicitConversionTester extends App {
import FractionConversions._
/*
* CASE 1 : Here, "fractionToDouble" implicit is called.
Why "intToFraction" was eligible but not called ?
*/
val a = 2 * Fraction(1,2)
/*
* CASE 2 : Works as expected.
Here, number "2" is converted to Fraction using "intToFraction" implicit.
*/
val b = 2.den
/*
* CASE 3: Why again "intToFraction" but not "fractionToDouble" ? Why ?
*/
val c = Fraction(4,5) * 3
println(a) // output : 0.1 FractionConversions.fractionToDouble called
println(b) // output : 1 FractionConversions.intToFraction called
println(c) // output : Value of Fraction is : 0.24000000000000005. FractionConversions.intToFraction called
}
我在上面的代码中有查询:
案例#1:对于语句 val a = 2 * Fraction(1,2)
,
为什么 fractionToDouble
implicit 在这里被调用,即使 intToFraction
在这种情况下也符合条件?
案例#3:对于语句val c = Fraction(4,5) * 3
,为什么调用intToFraction
?为什么 fractionToDouble
没有被使用?
这里,我试图复制书中提到的以下场景,所以出现了上述问题。
那么,我们是否应该总结一下,如果左侧操作数和 select 右侧操作数都符合转换条件,则编译器会避免进行转换?例如,在 (a*b)
的情况下,即使 a
和 b
都符合转换条件,a 总是被忽略并且 b 被转换为预期类型 ?
您的描述是正确的:对于表达式 a.f(b)
(注意 a*b
只是 a.*(b)
的运算符符号),适用于 b
的隐式转换允许要进行类型检查的表达式将优先于适用于 a
的隐式转换。出现这种情况是因为 a
的隐式转换(在语言标准中称为“视图”)只有在 *
(在 Int
中)不适用于 Fraction
时才能尝试(参见 SLS 7.3)...但是 SLS 6.6 中的适用性定义表明隐式视图足以适用。
因此,如果想将 2
隐式转换为 Fraction
以使用 Fraction.*
,则需要
(2: Fraction) * Fraction(1, 2)
同样,对于情况 3,如果您希望首先将分数隐式转换为 Double
:
(Fraction(4,5): Double) * 3
我是 Scala 的新手,我正在参考书 “Scala for Impatient - 第二版”。
我正在编写一个小代码,我在其中创建了一个 class Fraction
,它作为两个 Int 字段 num
(数字)和 den
(分母)。 class 有一个方法 *
执行 num
和 den
和 returns 新 Fraction
实例的乘法。
在这里,为了理解 implicit
的工作原理,我创建了一个对象 FractionConversions
,它有助于两个隐式方法 intToFraction
和 fractionToDouble
.
在测试 ImplicitConversionTester
中的代码时,我导入了 FractionConversions._
路径,因此编译器可以使用这两个隐式方法。
现在,参考代码,让图片更清晰。
package OperatorOverloadingAndImplicitConversion
class Fraction(n : Int, d : Int) {
private val num = this.n
private val den = this.d
def *(other : Fraction) = new Fraction(num*other.num, den*other.den)
override def toString: String = {
s"Value of Fraction is : ${(num*0.1)/den}"
}
}
object Fraction {
def apply(n: Int, d: Int): Fraction = new Fraction(n, d)
}
object FractionConversions {
implicit def fractionToDouble(f : Fraction): Double = {
println("##### FractionConversions.fractionToDouble called ...")
(f.num*0.1)/f.den
}
implicit def intToFraction(n : Int) : Fraction = {
println("##### FractionConversions.intToFraction called ...")
new Fraction(n,1)
}
}
object ImplicitConversionTester extends App {
import FractionConversions._
/*
* CASE 1 : Here, "fractionToDouble" implicit is called.
Why "intToFraction" was eligible but not called ?
*/
val a = 2 * Fraction(1,2)
/*
* CASE 2 : Works as expected.
Here, number "2" is converted to Fraction using "intToFraction" implicit.
*/
val b = 2.den
/*
* CASE 3: Why again "intToFraction" but not "fractionToDouble" ? Why ?
*/
val c = Fraction(4,5) * 3
println(a) // output : 0.1 FractionConversions.fractionToDouble called
println(b) // output : 1 FractionConversions.intToFraction called
println(c) // output : Value of Fraction is : 0.24000000000000005. FractionConversions.intToFraction called
}
我在上面的代码中有查询:
案例#1:对于语句
val a = 2 * Fraction(1,2)
, 为什么fractionToDouble
implicit 在这里被调用,即使intToFraction
在这种情况下也符合条件?案例#3:对于语句
val c = Fraction(4,5) * 3
,为什么调用intToFraction
?为什么fractionToDouble
没有被使用?
这里,我试图复制书中提到的以下场景,所以出现了上述问题。
那么,我们是否应该总结一下,如果左侧操作数和 select 右侧操作数都符合转换条件,则编译器会避免进行转换?例如,在 (a*b)
的情况下,即使 a
和 b
都符合转换条件,a 总是被忽略并且 b 被转换为预期类型 ?
您的描述是正确的:对于表达式 a.f(b)
(注意 a*b
只是 a.*(b)
的运算符符号),适用于 b
的隐式转换允许要进行类型检查的表达式将优先于适用于 a
的隐式转换。出现这种情况是因为 a
的隐式转换(在语言标准中称为“视图”)只有在 *
(在 Int
中)不适用于 Fraction
时才能尝试(参见 SLS 7.3)...但是 SLS 6.6 中的适用性定义表明隐式视图足以适用。
因此,如果想将 2
隐式转换为 Fraction
以使用 Fraction.*
,则需要
(2: Fraction) * Fraction(1, 2)
同样,对于情况 3,如果您希望首先将分数隐式转换为 Double
:
(Fraction(4,5): Double) * 3