I'm thinking of making a value class that has some guard on how it can be instantiated. For the sake of example, say I want a non-negative integer:
class NonNegInt private (val value: Int) extends AnyVal
object NonNegInt {
def apply(value: Int): Try[NonNegInt] = Try {
if (value >= 0) new NonNegInt(value) else throw new IllegalArgumentException("non-negative integers only!")
}
}
My only worry is that the private constructor may make it impossible for the scala compiler to treat the NonNegInt
as a primitive int
. Is this true?
If "treat as a primitive" here means "avoid allocation", then this indeed will not work, but not because of a private constructor.
As mentioned in Value Classes Guide
Another instance of this rule is when a value class is used as a type argument. For example, the actual Meter instance must be created for even a call to identity.
def identity[T](t: T): T = t
identity(Meter(5.0))
Basically, because identity[T] is parametrized, invoking it on a value type requires an allocation of an instance. Try[T] is the same situation: Try { ... }
"block" is an invocation of a parametrized function Try.apply[T]
with T being NonNegInt
. This call will require an allocation of NonNegInt
instance.