scalavalue-class

Does having a private constructor on a value class negate the benefits of a value class?


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?


Solution

  • 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.