Scala 中隐式转换如何工作?

问题描述 投票:0回答:3

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

源代码中这样写:

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

这段代码是如何调用的?

我可以理解另一个示例:“日期文字”的工作原理。

在。

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
get 以
Dec
作为参数传递消息
int
时,系统会寻找另一个可以处理该请求的方法,在本例中为
Dec(year:Int)

Q1。我对日期文字的理解正确吗?

Q2。它如何应用于 BigInt?

scala implicit-conversion
3个回答
17
投票

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

doSomethingWithBigInt(d:BigInt)=....

你用一个整数来称呼它:

doSomethingWithBigInt(10)

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

doSomethingWithBigInt(int2bigInt(10))

假设隐式 int2bigInt 在范围内


14
投票

隐式内容的要点是在显然只有一种正确的方法时填充无聊的样板内容。

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

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)

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

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

让我们看一下每个示例。

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

(注意:

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


2
投票

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

© www.soinside.com 2019 - 2024. All rights reserved.