I'd like to use case classes to describe the types of my data more expressively so that to benefit from higher static correctness. The goal is to have 100% static certainty that any Age
value in existence always contains a valid human age (leaving aside the fact that encapsulation rules can be bypassed using reflection).
For example, instead of using Int
to store ages of persons, I have:
case class Age(x: Int) extends AnyVal
def mkAge(x: Int) = if (0 <= x && x <= 150) Some(Age(x)) else None
def unwrapAge(age: Age) = age.x
however, this implementation suffers from the fact that Age
can still be instantiated without going through mkAge
and unwrapAge
.
Next, I tried to make the constructor private:
case class Age private(x: Int) extends AnyVal
object Age {
def make(x: Int) = if (0 <= x && x <= 150) Some(Age(x)) else None
def unwrap(age: Age) = age.x
}
however, while this does prevent Age
from being instantiated using new (e.g. new Age(3)
), the autogenerated apply(x: Int)
in object Age
is still easily accessible.
So, here's the question: how to hide both the constructor as well as the default apply
method in the companion object from anything but Age.make
or mkAge
?
I'd like to avoid having to use a regular (non-case
) class and correctly replicate the auto-generated methods in class Age
and object Age
manually.
I think Age just don't need to be a case class. Because it is a Value Class you don't need to override equals and hashcode, also it has only one field so there is no benefit from copy constructor. And you can do nothing with apply() in companion object. If you still want to use case class you can add require but it doesn't solve you problem of instantiation.