Your example code is making your actual question ambiguous. Your example code wraps the Int
in a Try
. If instead of using Try
, you used a require
statement in the companion object, then it's my understanding the code below would work (without losing the "primitive" benefits extending AnyVal
offers). This would give you a runtime exception if/when there is an attempt to produce a negative value. The code uses a private
constructor on the case class extending AnyVal
. Then it uses the case class's companion object's apply
method to enforce runtime constraints via a require
statement.
If you really need to wrap the value using a Try
, you can provide an additional companion object constructor to wrap apply to capture the exception. However, as is pointed out in other answers, you lose the AnyVal
"primitive" quality when it is "contained" by a Try
, Option
, Either
, etc.
WARNING: The code below will not compile in the REPL/Scala Worksheet. A case class extending AnyVal must be a top-level class; i.e. cannot be nested within the scope of another class, trait, or object. And both the REPL and Scala Worksheet are implemented by pushing all the code into an invisible containing class before executing.
object PositiveInt {
def apply(value: Int): PositiveInt = {
require(value >= 0, s"value [$value] must be greater than or equal to 0")
new PositiveInt(value)
}
def tryApply(value: Int): Try[PositiveInt] =
Try(apply(value))
}
case class PositiveInt private(value: Int) extends AnyVal
val positiveTestA = PositiveInt(0)
val positiveTestB = PositiveInt(1)
val positiveTestD = PositiveInt.tryApply(-1)) //returns Failure
val positiveTestD = Try(PositiveInt(-1)) //returns Failure
val positiveTestC = PositiveInt(-1) //throws required exception