1. Overview
In this tutorial, we’ll learn about implicit conversions. We’ll see how they reduce boilerplate code and how they can be used to add additional methods to existing classes.
2. Implicit Conversions
Implicit conversions give the Scala compiler the ability to convert one type into another.
We can also use them to add additional methods to existing classes. However, since Scala 2.10, we should use implicit classes for this use case.
3. A Length Use Case
Let’s imagine that we have different types that represent length units:
case class Centimeters(value: Double) extends AnyVal
case class Meters(value: Double) extends AnyVal
case class Kilometers(value: Double) extends AnyVal
We would like to use them interchangeably without any boilerplate code.
4. Implicit Conversion Method
If we want to use Meters in places where Centimeters are required, we need to provide an implicit conversion from Meters into Centimeters. We can do it by creating an implicit method, which takes Meters and returns Centimeters:
implicit def meters2centimeters(meters: Meters): Centimeters =
Centimeters(meters.value * 100)
val centimeters: Centimeters = Meters(2.5)
centimeters shouldBe Centimeters(250)
The Scala compiler knows that Meters is not a subtype of Centimeters. However, instead of a compilation error, it checks if there is an implicit conversion available that converts Meters into Centimeters.
Therefore we have provided an implicit meters2centimeters method, we are able to assign the Meters(2.5) to variable centimeters of type Centimeters.
5. Implicit Conversion Function
An implicit function is another way of providing an implicit conversion:
implicit val kilometers2meters: Kilometers => Meters =
kilometers => Meters(kilometers.value * 1000)
val meters: Meters = Kilometers(2.5)
meters shouldBe Meters(2500)
This time we have provided an implicit kilometers2meters function and are now able to treat Kilometers as Meters.
6. Extension Methods
An implicit conversion can also be used to add additional methods to existing classes. So let’s add centimeters, meters, and kilometers methods for creating length units to Double:
class LengthSyntax(value: Double) {
def centimeters = Centimeters(value)
def meters = Meters(value)
def kilometers = Kilometers(value)
}
implicit def double2richSyntax(value: Double): LengthSyntax =
new LengthSyntax(value)
val length: Double = 2.5
length.centimeters shouldBe Centimeters(length)
length.meters shouldBe Meters(length)
length.kilometers shouldBe Kilometers(length)
In this case, the compiler is looking for an implicit conversion that converts Double into anything that has centimeters, meters, or kilometers methods. Therefore we provided an implicit double2richSyntax method, and the compiler knows what to do.
7. Limitations
Unfortunately, implicit conversions have some limitations:
- They cannot take multiple non-implicit arguments
- They cannot chain multiple implicit conversions
7.1. Non-implicit Arguments
They cannot take multiple non-implicit arguments:
implicit def meters2centimeters(meters: Meters, secondArg: Boolean): Centimeters =
Centimeters(meters.value * 100)
val centimeters: Centimeters = Meters(2.5)
The above code won’t compile because we added the second non-implicit argument. The compiler is not able to treat Meters as Centimeters:
[error] type mismatch; [error] found : com.baeldung.scala.implicitconversions.Meters [error] required: com.baeldung.scala.implicitconversions.Centimeters [error] val centimeters: Centimeters = Meters(2.5)
However, having an additional implicit argument is not a problem:
implicit val boolean = true
implicit def meters2centimeters(meters: Meters)(implicit secondArg: Boolean): Centimeters =
Centimeters(meters.value * 100)
val centimeters: Centimeters = Meters(2.5)
7.2. Chain Implicit Conversions
We cannot chain multiple implicit conversions:
implicit def kilometers2meters(kilometers: Kilometers): Meters =
Meters(kilometers.value * 1000)
implicit def meters2centimeters(meters: Meters): Centimeters =
Centimeters(meters.value * 100)
val centimeters: Centimeters = Kilometers(2.5)
The Scala compiler can take only one implicit conversion into consideration. Consequently, we cannot assign Kilometers into a centimeters variable of Centimeters type:
[error] type mismatch; [error] found : com.baeldung.scala.implicitconversions.Kilometers [error] required: com.baeldung.scala.implicitconversions.Centimeters [error] val centimeters: Centimeters = Kilometers(2.5)
Although, we can have implicit conversions from Kilometers into Meters and Meters into Centimeters.
8. Warnings
As we saw, implicit conversions have great powers. Nonetheless, with great power comes great responsibility. Therefore the Scala compiler will always warn us about using implicit conversions:
[warn] implicit conversion method meters2centimeters should be enabled
We can enable implicit conversion in the whole project by setting the compiler option in our build.sbt:
scalacOptions += "-language:implicitConversions"
On the other hand, if we want to enable it only for a particular class or method we can do it using a single import:
import scala.language.implicitConversions
9. Conclusion
In this short article, we explored implicit conversions*.* We saw how they reduce boilerplate code and how they can be used to add methods to existing classes.
As always, the full source code of the article is available over on GitHub.