对于第(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)
...