Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Value classes, universal traits and the necessity of instantiation

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 has defs 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

First Question

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?

Second Question

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?

Edit

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?

like image 459
oxbow_lakes Avatar asked Nov 06 '17 15:11

oxbow_lakes


People also ask

What are values classes?

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.

What are value classes in Java?

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.

What value is a class of value?

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.

What is Scala AnyVal?

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.


1 Answers

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.

like image 87
Yuval Itzchakov Avatar answered Nov 09 '22 18:11

Yuval Itzchakov