我想使用案例类来更明确地描述我的数据类型,以便从更高的静态正确性中受益。目标是100%静态确定存在的任何Age
值始终包含有效的人类年龄(不考虑使用反射绕过封装规则的事实)。
例如,我没有使用Int
存储年龄,而是:
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
但是,此实现受到以下事实的影响:Age
仍然可以在不经过mkAge
和unwrapAge
的情况下进行实例化。
接下来,我尝试将构造函数设为私有:
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
}
但是,虽然此 阻止Age
使用new(例如new Age(3)
)进行实例化,但apply(x: Int)
中自动生成的object Age
仍然是容易到达。
所以,问题是:如何隐藏配对对象中的构造函数和默认apply
方法,除了Age.make
或mkAge
之外的其他任何内容?
我希望避免使用常规(非case
)类并正确地在class Age
和object Age
中手动复制自动生成的方法。
答案 0 :(得分:1)
你快到了那里:
case class Age private(private val x:Int) extends AnyVal
object Age {
def mkAge(x:Int) = if(0<=x && x<=150) Some(Age(x)) else None
def unwrapAge(age:Age) = age.x
}
注意case类构造函数中的额外private val
。
答案 1 :(得分:1)
所以,这里有一个问题:除了Age.make或mkAge之外,如何隐藏伴随对象中的构造函数和默认apply方法?
我想避免使用常规(非案例)类并正确地手动复制Age类和对象Age中的自动生成方法。
我认为这是不可能的,但https://stackoverflow.com/a/25538287/9204详述了一个(相当不重要的)解决方案。
答案 2 :(得分:0)
我认为Age不需要成为案例类。因为它是一个Value Class,所以你不需要重写equals和hashcode,它也只有一个字段,所以复制构造函数没有任何好处。你无法在伴侣对象中使用 apply()做任何事情。如果您仍想使用案例类,可以添加 require ,但它不能解决实例化问题。
object A extends App {
import Age._
println(mkAge(150))
println(mkAge(151))
//println(new Age(51)) //Error!
val a = mkAge(15) match {
case Some(Age(x)) => x
case None => 0
}
print(a)
}
class Age private(val x: Int) extends AnyVal {
override def toString = s"A($x)"
}
object Age {
def mkAge(x: Int) = if (0 <= x && x <= 150) Some(new Age(x)) else None
def unwrapAge(age: Age) = age.x
def unapply(age: Age) = if (age == null) None else Some(age.x)
}