据我所知,没有直接的方法可以覆盖案例类构造函数。但是,假设实际数据类型不是简单的 int,您可以执行一些考虑无效状态的类型,如下所示:
sealed abstract class NonNegativeInt { def isValid: Boolean }
final case class ValidNonNegativeInt(i: Int) extends NonNegativeInt { override def isValid: Boolean = true }
final case object InvalidNonNegativeInt extends NonNegativeInt { override def isValid: Boolean = false }
object NonNegativeInt {
def apply(i: Int): NonNegativeInt = if (i < 0) InvalidNonNegativeInt else ValidNonNegativeInt(i)
}
这很简单:
scala> NonNegativeInt(0)
res5: NonNegativeInt = ValidNonNegativeInt(0)
scala> NonNegativeInt(-1)
res6: NonNegativeInt = InvalidNonNegativeInt
然后你甚至可以进行模式匹配:
val ni = NonNegativeInt(10)
ni match {
case ValidNonNegativeInt(i) => println(s"valid $i")
case InvalidNonNegativeInt => println(s"invalid")
}
然后您可以使用 map/flatMap 等进一步扩展您的功能。
当然它仍然不能保护你免受负面情况的影响:
scala> ValidNonNegativeInt(-10)
res7: ValidNonNegativeInt = ValidNonNegativeInt(-10)
但例如 scala 选项也不会覆盖 Some() 情况下允许无效值的构造函数:
scala> Option(null)
res8: Option[Null] = None
scala> Some(null)
res9: Some[Null] = Some(null)
除非没有关键用例,否则对于简单的 Int,我会保持原样,并确保其在使用中的正确性。对于更复杂的结构,上述方法非常有用。
注意:我故意不使用你的 Max(0, n) 方式,因为在这种情况下
会导致比它解决的问题更多的问题。假设某事,并且
在后台交换数据是不好的做法。想象一下你会有一个
在您的代码其他地方的某个地方出现错误,这将使用您的
使用 Max(0, n) 实现。如果输入数据是 -10,很可能,
问题是由传入数据中的其他问题引起的。当你
将其更改为默认值 0,即使输入为 -10,稍后当您
将分析日志、转储或调试输出,你会错过这样一个事实
是-10。
我认为其他解决方案:
@flavian 解决方案是最合乎逻辑的。显式功能/验证
@Cyrille Corpet:非常 Java 风格
@jwvh 解决方案将占用双倍的内存占用,因为它将在内存中占用两个 Ints。并且也不会防止覆盖:
scala> case class NonNegativeInt1(private val x:Int)(implicit val i:Int = Math.max(0,x)) {
| override def toString: String = s"NonNegativeInt1($x, $i)"
| }
defined class NonNegativeInt1
scala> NonNegativeInt1(5)
res10: NonNegativeInt1 = NonNegativeInt1(5, 5)
scala> NonNegativeInt1(-5)
res11: NonNegativeInt1 = NonNegativeInt1(-5, 0)
scala> NonNegativeInt1(-5)(-5)
res12: NonNegativeInt1 = NonNegativeInt1(-5, -5)