有人可以解释我在Scala中的隐式转换吗?

时间:2010-05-18 22:08:07

标签: scala implicit-conversion

更具体地说,BigInt如何将int转换为BigInt?

在源代码中显示:

...
implicit def int2bigInt(i: Int): BigInt = apply(i)
...

如何调用此代码?

我可以理解其他示例: "Date literals" 的工作原理。

val christmas = 24 Dec 2010  

定义者:

implicit def dateLiterals(date: Int) = new {
  import java.util.Date  
  def Dec(year: Int) = new Date(year, 11, date)
}

int获取传递消息Dec且参数为int时,系统会查找另一种可以处理请求的方法,在本例中为Dec(year:Int)

Q1。我是否正确理解日期文字?

Q2。它如何适用于BigInt?

谢谢

3 个答案:

答案 0 :(得分:14)

当提供的类型与预期类型不匹配时,Scala编译器会查找标记为implicit的范围内的任何方法,该方法将提供的类型作为参数并返回期望的类型作为结果。如果找到,它会在中间插入对方法的调用。 在BigInt案例中,假设你有一个方法

doSomethingWithBigInt(d:BigInt)=....

你用整数称它:

doSomethingWithBigInt(10)

由于类型不匹配,Scala编译器将生成:

doSomethingWithBigInt(int2bigInt(10))

假设隐式int2bigInt在范围

答案 1 :(得分:11)

隐含的东西的意思是在显然只有一种正确的方法时填写无聊的样板材料。

对于隐式参数,编译器会从上下文中插入一个必须是您所考虑的参数。例如,

case class TaxRate(rate: BigDecimal) { }
implicit var sales_tax = TaxRate(0.075)
def withTax(price: BigDecimal)(implicit tax: TaxRate) = price*(tax.rate+1)

scala> withTax(15.00)
res0: scala.math.BigDecimal = 16.1250

由于我们已将税率标记为隐含参数,并提供了可在需要时填写的隐式变量,因此我们无需指定税率。编译器会自动填写withTax(15.00)(sales_tax)

对于隐式转换,编译器会查找可以获取其类型的方法并将其转换为所需的类型。在正常情况下,此转换无法链接,因此您必须在一个步骤中获得所需内容。

有两种情况可能会发生隐式转换。一个是在方法调用的参数中 - 如果类型错误,但它可以转换为正确的类型(以一种方式),那么编译器将为您转换。另一个是在方法调用的 presence 中 - 如果实际使用的类型没有可用的方法,但是你可以将它转换为具有该方法的类型,那么转换将发生,然后将调用该方法。

让我们看一下每个例子。

implicit def float2taxrate(f: Float) = TaxRate(BigDecimal(f))
scala> withTax(15.00)(0.15f)
res1: scala.math.BigDecimal = 17.250000089406967200

在这里,我们将明确的税率称为0.15f。这与参数不匹配,参数必须是TaxRate类型,但编译器发现我们可以使用隐式float2taxrate将浮点数转换为税率。所以它为我们做了,调用withTax(15.00)(float2taxrate(0.15f))

现在是另一个例子。

class Currency(bd: BigDecimal) {
  def rounded = bd.setScale(2,BigDecimal.RoundingMode.HALF_EVEN)
}
implicit def bigdec2currency(bd: BigDecimal) = new Currency(bd)
scala> withTax(15.00)(0.15f).rounded
res66: scala.math.BigDecimal = 17.25

BigDecimal没有rounded方法,因此withTax(15.00)(0.15f)不应该调用一个(因为它返回BigDecimal)。但我们已经定义了一个Currency,它有一个rounded方法,并转换为Currency,因此隐式转换会填充所有细节:bigdec2currency(withTax(15.00)(0.15f)).rounded

如果从Int转换为BigInt,编译器将在例如尝试添加7 + BigInt(5)时使用它。这不能正常工作 - 7IntInt不知道如何将自己添加到BigInt。但是BigInt有一个方法+可以将自己添加到另一个BigInt。编译器看到,只要它可以将7转换为BigInt,它就可以使用该方法。隐式转换允许转换,因此它会将7 + BigInt(5)转换为int2bigInt(7)+BigInt(5)

(注意:int2bigInt是在BigInt内定义的,因此要使用import BigInt._,它必须遵循apply(i: Int)方法BigInt 1}} object,这是让你编写BigInt(5)并使其工作的原因(而不是像Java中的BigInteger一样传递字符串)。)

答案 2 :(得分:0)

补充@GClaramunt的答案。

因为通过查看完整的示例,可以更轻松地理解和掌握此概念:

// define a class
case class Person(firstName: String, lastName: String)

// must import this to enable implicit conversions
import scala.language.implicitConversions

// define the implicit conversion. String to Person in this case
implicit def stringToPerson(name:String) = {
  val fields = name.split(" ");
  Person(fields(0), fields(1))
}

// method using the implicit conversion  
def getPerson(fullName:String): Person = fullName

val fooBar = getPerson("foo bar")
println(fooBar.getClass())  // class Person
println(fooBar.firstName)  // foo
println(fooBar.lastName)  // bar

我希望这个例子可以阐明为什么以及如何使用隐式转换(我认为将String转换为Person并不是很有道理,但是值得说明)。