Scala 2.10 introduces value classes, which you specify by making your class extend AnyVal
. There are many restrictions on value classes, but one of their huge advantages is that they allow extension methods without the penalty of creating a new class: unless boxing is required e.g. to put the value class in an array, it is simply the old class plus a set of methods that take the class as the first parameter. Thus,
implicit class Foo(val i: Int) extends AnyVal {
def +*(j: Int) = i + j*j
}
unwraps to something that can be no more expensive than writing i + j*j
yourself (once the JVM inlines the method call).
Unfortunately, one of the restrictions in SIP-15 which describes value classes is
- The underlying type of C may not be a value class.
If you have a value class that you can get your hands on, say, as a way to provide type-safe units without the overhead of boxing (unless you really need it):
class Meter(val meters: Double) extends AnyVal {
def centimeters = meters*100.0 // No longer type-safe
def +(m: Meter) = new Meter(meters+m.meters) // Only works with Meter!
}
then is there a way to enrich Meter
without object-creation overhead? The restriction in SIP-15 prevents the obvious
implicit class RichMeter(m: Meter) extends AnyVal { ... }
approach.
In order to extend value classes, you need to recapture the underlying type. Since value classes are required to have their wrapped type accessible (val i
not just i
above), you can always do this. You can't use the handy implicit class
shortcut, but you can still add the implicit conversion longhand. So, if you want to add a -
method to Meter
you must do something like
class RichMeter(val meters: Double) extends AnyVal {
def -(m: Meter) = new Meter(meters - m.meters)
}
implicit def EnrichMeters(m: Meter) = new RichMeter(m.meters)
Note also that you are allowed to (freely) rewrap any parameters with the original value class, so if it has functionality that you rely on (e.g. it wraps a Long
but performs complicated bit-mixing), you can just rewrap the underlying class in the value class you're trying to extend wherever you need it.
(Note also that you'll get a warning unless you import language.implicitConversions
.)
Addendum: in Scala 2.11+, you may make the val
private; for cases where this was done, you will not be able to use this trick.