Can someone explain me implicit conversions in Scala?
And more specifically how does the BigInt works for convert int to BigInt?
In the source code it reads:
...
implicit def int2bigInt(i: Int): BigInt = apply(i)
...
How is this code invoked?
开发者_StackOverflow社区I can understand how this other sample: "Date literals" works.
In.
val christmas = 24 Dec 2010
Defined by:
implicit def dateLiterals(date: Int) = new {
import java.util.Date
def Dec(year: Int) = new Date(year, 11, date)
}
When int
get's passed the message Dec
with an int
as parameter, the system looks for another method that can handle the request, in this case Dec(year:Int)
Q1. Am I right in my understanding of Date literals?
Q2. How does it apply to BigInt?
Thanks
When a the provided type doesn't match the expected type, the Scala compiler looks for any method in scope marked implicit that takes the provided type as parameter and returns the expected type as a result. If found, it inserts the call to the method in between. In the BigInt case, say you have a method
doSomethingWithBigInt(d:BigInt)=....
And you call it with an integer:
doSomethingWithBigInt(10)
As the types don't match, the Scala compiler will generate:
doSomethingWithBigInt(int2bigInt(10))
Assuming the implicit int2bigInt is in scope
The point of implicit stuff is to fill in boring boilerplate stuff when there is clearly only one right way to do it.
In the case of implicit parameters the compiler inserts a parameter from context that must be what you were thinking of. For example,
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
Since we've marked the tax rate as an implicit parameter, and provided an implicit variable that can be filled in when needed, we don't need to specify the tax rate. The compiler automatically fills in withTax(15.00)(sales_tax)
In the case of implicit conversions, the compiler looks for a method that can take a type that it has and convert it to the type that is needed. This conversion cannot be chained under normal circumstances, so you have to get to what you need in one step.
There are two cases where implicit conversions are likely to come into play. One is in the parameter of a method call--if the type is wrong, but it can be converted to the right type (in exactly one way), then the compiler will convert for you. The other is in the presence of a method call--if the type actually used doesn't have the method available, but you could convert it to a type that does have that method, then the conversion will take place and then the method will be called.
Let's look at an example of each.
implicit def float2taxrate(f: Float) = TaxRate(BigDecimal(f))
scala> withTax(15.00)(0.15f)
res1: scala.math.BigDecimal = 17.250000089406967200
Here, we call an explicit tax rate of 0.15f
. That doesn't match the parameter, which must be of type TaxRate
, but the compiler sees that we can turn floats into tax rates using the implicit float2taxrate
. So it does it for us, calling withTax(15.00)(float2taxrate(0.15f))
Now the other example.
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 doesn't have a rounded
method, so withTax(15.00)(0.15f)
shouldn't be able to call one (as it returns a BigDecimal
). But we've defined a Currency
that does have a rounded
method, and a conversion to Currency
, so the implicit conversion fills in all the details: bigdec2currency(withTax(15.00)(0.15f)).rounded
.
In the case of the conversion from Int
to BigInt
, the compiler will use it when, for example, it tries to add 7 + BigInt(5)
. This isn't going to work normally--7
is an Int
and Int
doesn't know how to add itself to BigInt
. But BigInt
has a method +
that can add itself to another BigInt
. And the compiler sees that if only it could convert 7
to a BigInt
, it could use that method. The implicit conversion allows that conversion, so it translates 7 + BigInt(5)
into int2bigInt(7)+BigInt(5)
.
(Note: int2bigInt
is defined inside BigInt
, so to use it you have to import BigInt._
. And it in turn defers to the apply(i: Int)
method of the BigInt
object, which is what lets you write BigInt(5)
and have it work (rather than having to pass a string as with BigInteger
in Java).)
Complementing @GClaramunt's answer.
Because it's way simpler to understand and grasp this concept by seeing a full example:
// 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
I hope this example clarifies why and how one would want to use implicit conversions (not that I think converting String
to Person
makes a lot of sense but it's worth the illustration).
精彩评论