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.