If I have this value class:
class ActionId(val value: Int) extends AnyVal
Then, in all the examples below, an object will be allocated for
In all three cases there will be no boxing at all.
It's quite easy to check by your self:
class ActionId(val value: Int) extends AnyVal
object Foo {
def someFunction(): ActionId = {
new ActionId(123)
}
}
Now lets run scalac, and we will have a bunch of class files (files with bytecode). The one that we need is Foo\$.
» javap Foo\$
Compiled from "Boxing.scala"
public final class Foo$ extends java.lang.Object{
public static final Foo$ MODULE$;
public static {};
public int someFunction();
}
As you can see, even if value class leaks from function generally it wouldn't be boxed.
case class Post(id: ActionId, notion: String)
object Foo2 {
def someFunction(): Post = {
Post(new ActionId(123), "So ActionID will be boxed?")
}
}
scalac => javap:
» javap Post
Compiled from "Boxing.scala"
public class Post extends java.lang.Object implements scala.Product,scala.Serializable{
public static scala.Function1 tupled();
public static scala.Function1 curried();
public int id();
public java.lang.String notion();
public Post copy(int, java.lang.String);
public int copy$default$1();
public java.lang.String copy$default$2();
public java.lang.String productPrefix();
public int productArity();
public java.lang.Object productElement(int);
public scala.collection.Iterator productIterator();
public boolean canEqual(java.lang.Object);
public int hashCode();
public java.lang.String toString();
public boolean equals(java.lang.Object);
public Post(int, java.lang.String);
}
As you can see id here represented as plain int (e.g. third method).
Finally, will value class boxed if the object with a value class member is not returned (doesn't really escape the scope)?
case class Post(id: ActionId, notion: String)
object Foo3 {
def anotherFunction(): Unit {
val post = Post(new ActionId(123), "Will be boxed?")
}
}
If we look closely at bytecode of the method, here is what we will see:
Code:
Stack=4, Locals=2, Args_size=1
0: new #15; //class Post
3: dup
4: bipush 123
6: ldc #17; //String Will be boxed?
8: invokespecial #20; //Method Post."":(ILjava/lang/String;)V
11: astore_1
12: return
LocalVariableTable:
Start Length Slot Name Signature
0 13 0 this LFoo3$;
12 0 1 post LPost;
There is no boxing of int in ActionId. If it would box you would see something like this one:
Code:
Stack=5, Locals=2, Args_size=1
0: new #15; //class Post
3: dup
4: new #17; //class ActionId
7: dup
8: bipush 123
10: invokespecial #20; //Method ActionId."":(I)V
13: ldc #22; //String Will be boxed?
15: invokespecial #25; //Method Post."":(LActionId;Ljava/lang/String;)V
18: astore_1
19: return
LocalVariableTable:
Start Length Slot Name Signature
0 20 0 this LFoo3$;
19 0 1 post LPost;
You see, the difference is bipush 123
versus
4: new #17; //class ActionId
7: dup
8: bipush 123
10: invokespecial #20; //Method ActionId."":(I)V