对于第(1)点,Option确实应该足够了。这是因为虽然 scala 支持 null 值,但它主要是为了与 Java 兼容。Scala 代码不应该包含 null 值,并且它应该被限制在非常本地化的地方,并尽快转换为一个选项(好的 scala 代码永远不会让 null 值传播)。所以在惯用的 scala 中,如果一个字段或参数不是类型Option,这实际上意味着它是必需的。
现在,还有(实验性的,据我所知从未完全支持)NotNull特征。请参阅NotNull 特征在 2.8 中如何工作以及是否有人实际使用它?
对于第 (2) 点,scala 2.10 引入了值类。使用它们,您可以定义自己的包装类Int而无需运行时开销,并按照您认为合适的方式实现其运算符。唯一可以进行运行时检查的地方是从正常转换Int为您的NonNegativeInt(如果 int 为负数则抛出异常)。请注意,每次创建 new 时都会执行此检查NonNegativeInt,这也意味着每次执行操作时都会执行此检查,因此会产生非空运行时影响。但是 Pascal 处于同样的情况(范围检查是在 Pascal 中在运行时执行的)所以我想你可以接受。
更新:这是NonNegativeInt(此处重命名为UInt)的示例实现:
object UInt {
def apply( i: Int ): UInt = {
require( i >= 0 )
new UInt( i )
}
}
class UInt private ( val i: Int ) extends AnyVal {
override def toString = i.toString
def +( other: UInt ) = UInt( i + other.i)
def -( other: UInt ) = UInt( i - other.i)
def *( other: UInt ) = UInt( i * other.i)
def /( other: UInt ) = UInt( i / other.i)
def <( other: UInt ) = i < other.i
// ... and so on
}
以及 REPL 中的一些示例用法:
scala> UInt(123)
res40: UInt = 123
scala> UInt(123) * UInt(2)
res41: UInt = 246
scala> UInt(5) - UInt(8)
java.lang.IllegalArgumentException: requirement failed
at scala.Predef$.require(Predef.scala:221)
at UInt$.apply(<console>:15)
...