In the specification of value classes, it says:
A value class can only extend universal traits and cannot be extended itself. A universal trait is a trait that extends
Any
, only hasdef
s as members, and does no initialization. Universal traits allow basic inheritance of methods for value classes, but they incur the overhead of allocation. For example
trait Printable extends Any {
def print(): Unit = println(this)
}
class Wrapper(val underlying: Int) extends AnyVal with Printable
val w = new Wrapper(3)
w.print() // actually requires instantiating a Wrapper instance
Now, I would take this to mean that the following (probably) does not require instantiation:
trait Marker extends Any
class Wrapper(val underlying: Int) extends AnyVal with Marker {
def print(): Unit = println(this) //unrelated to Marker
}
val w = new Wrapper(3)
w.print() //probably no instantiation as print is unrelated to Marker
Am I correct?
And I would think there is an even chance as to whether this requires instantiation or not:
trait Printable extends Any {
def print(): Unit //no implementation
}
class Wrapper(val underlying: Int) extends AnyVal with Printable {
override def print() = println(this) //moved impl to value class
}
val w = new Wrapper(3)
w.print() // possibly requires instantiation
On the balance of probability, I would also think that no instantiation would be needed - am I correct?
I'd not thought about the exact implementation of print()
in the example:
def print(): Unit = println(this)
Let's say that I used the following instead:
def print(): Unit = println(underlying)
Would these cause instantiations?
Value classes are a new mechanism which help to avoid allocating run time objects. AnyVal define value classes. Value classes are predefined, they coincide to the primitive kind of Java-like languages. There are nine predefined value types : Double, Float, Long, Int, Short, Byte, Char, Unit, and Boolean.
A value class declaration introduces a class whose instances are value objects. An identity object is a class instance or array that does have identity—the traditional behavior of objects in Java. An identity object can mutate its non- final fields and is associated with a synchronization monitor.
Fundamentally, a value class is one that wraps around a very simple type or a simple value, like Int , Boolean , etc. What that means is that, at compile time, you see the value class and use the value class, but at the time bytecode could get generated, you are actually using the underlying simple type.
AnyVal is the root class of all value types, which describe values not implemented as objects in the underlying host system. Value classes are specified in Scala Language Specification, section 12.2. The standard implementation includes nine AnyVal subtypes: scala. Double, scala.
Am I correct?
No, we can see it if we emit the final compilation output with -Xprint:jvm
:
<synthetic> object F$Wrapper extends Object {
final def print$extension($this: Int): Unit =
scala.Predef.println(new com.testing.F$Wrapper($this));
This is due to the fact println
has a type signature requiring Any
, so we're shooting ourselves in the foot here since we're effectively "treating the value class ttpe as another type".
Although the call is dispatched to the static method call:
val w: Int = 3;
F$Wrapper.print$extension(w)
We're still incurring the allocation inside print$extension
.
If we stray away from using Wrapper.this
, then your first assumption is indeed correct and we can see the compiler happily unwrap Wrapper
:
<synthetic> object F$Wrapper extends Object {
final def print$extension($this: Int): Unit =
scala.Predef.println(scala.Int.box($this));
And the call site now looks like this:
val w: Int = 3;
com.testing.F$Wrapper.print$extension(w)
This is valid for both of your examples now, as there is no need for any dynamic dispatch on the created interface.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With