【问题标题】:Can someone explain me implicit conversions in Scala?有人可以解释一下 Scala 中的隐式转换吗?
【发布时间】:2010-05-18 22:08:07
【问题描述】:

更具体地说,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)
}

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

第一季度。我对日期文字的理解是否正确?

第二季度。它如何应用于 BigInt?

谢谢

【问题讨论】:

标签: scala implicit-conversion


【解决方案1】:

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

doSomethingWithBigInt(d:BigInt)=....

你用一个整数来调用它:

doSomethingWithBigInt(10)

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

doSomethingWithBigInt(int2bigInt(10))

假设隐式 int2bigInt 在范围内

【讨论】:

  • 为了澄清,它使用了一个隐式 如果它会使原本不会编译的代码这样做,而不仅仅是如果有一个隐式接受找到的类型作为其参数.
  • 范围是一个红鲱鱼。 Scala 将在原始类的伴生对象中搜索此类隐含,如果已推断出目标类,则在目标类的伴生对象中搜索。在这种情况下,由于目标类显式为 BigInt,因此将搜索 BigInt 的对象伴侣以查找隐式。
  • 我不想详细介绍范围 :)
  • @Daniel:在这种情况下,正如您所说,范围不是问题。但是如果 OP 希望自己实现一些隐式转换,我必须同意@GClaramunt 的观点,至少值得一提。
【解决方案2】:

隐含的东西的重点是在显然只有一种正确方法的情况下填充无聊的样板文件。

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

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)

隐式转换的情况下,编译器会寻找一种方法,该方法可以采用它拥有的类型并将其转换为所需的类型。此转换在正常情况下无法链接,因此您必须在一个步骤中获得所需的内容。

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

让我们看一个例子。

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) 时使用它。这不会正常工作--7Int,而Int 不知道如何将自己添加到BigInt。但是BigInt 有一个方法+ 可以将自己添加到另一个BigInt。并且编译器看到,只要它可以将7 转换为BigInt,它就可以使用该方法。隐式转换允许这种转换,因此它将7 + BigInt(5) 转换为int2bigInt(7)+BigInt(5)

(注意:int2bigInt 定义在 BigInt 内部,所以要使用它,你必须 import BigInt._。它又遵循 BigInt 对象的 apply(i: Int) 方法,这就是让你编写BigInt(5) 并让它工作(而不是像Java 中的BigInteger 那样传递字符串)。

【讨论】:

    【解决方案3】:

    补充@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 很有意义,但值得说明)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-22
      • 2016-12-12
      • 2011-03-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多