Reputation: 1205
As I understand, if you create an array of a value class, you're actually creating an array of objects rather than the wrapped primitive. What's the reason behind this?
source:
class Wrapper(val underlying: Int) extends AnyVal
class Main {
val i: Int = 1
val w: Wrapper = new Wrapper(1)
val wrappers = Array[Wrapper](new Wrapper(1), new Wrapper(2))
val ints = Array[Int](1, 2)
}
javap output:
public class Main {
public int i();
public int w();
public Wrapper[] wrappers(); // <----why can't this be int[] as well
public int[] ints();
public Main();
}
Upvotes: 3
Views: 96
Reputation: 15086
One of the constraints of value classes is that x.isInstanceOf[ValueClass]
should still work correctly. Where correctly means: transparently, without the programmer having to be aware when values may or may not be boxed.
If an Array[Meter]
would be represented as an Array[Int]
at runtime the following code would not work as expected, because the information that the ints in the array are actually meters is lost.
class Meter(val value: Int) extends AnyVal
def centimeters[A](as: Array[A]) = as.collect{ case m: Meter => m.value * 100 }
Note that if you have val m = new Meter(42); m.isInstanceOf[Meter]
then the compiler knows that m
is a Meter
even though it's an Int
at runtime and he can inline the isInstanceOf
call to true
.
Also note that this wouldn't work for arrays. If you would box the values in the array on demand you'd have to create a new array, which wouldn't be transparent to the programmer because arrays are mutable and use reference equality. It would also be a disaster for performance with large arrays.
Upvotes: 6
Reputation: 3238
According to https://docs.scala-lang.org/overviews/core/value-classes.html:
Allocation Summary
A value class is actually instantiated when:
- a value class is treated as another type.
- a value class is assigned to an array.
- doing runtime type tests, such as pattern matching.
[...]
Another situation where an allocation is necessary is when assigning to an array, even if it is an array of that value class. For example,
val m = Meter(5.0)
val array = Array[Meter](m)
The array here contains actual Meter instances and not just the underlying double primitives.
This has probably something to do with type erasure, or simply because you're creating an array of a specific type, which doesn't allow one type to be treated as another. In any case, it's a technical limitation.
Upvotes: 3