11

我仍在学习 Scala,但我认为有趣的一件事是 Scala 模糊了方法和字段之间的界限。例如,我可以建立一个这样的类......

class MutableNumber(var value: Int)

这里的关键是构造函数参数中的 var 自动允许我像 java 中的 getter/setter 一样使用“value”字段。

// use number...
val num = new MutableNumber(5)
num.value = 6
println(num.value)

如果我想添加约束,我可以通过切换到使用方法代替实例字段来实现:

// require all mutable numbers to be >= 0
class MutableNumber(private var _value: Int) {
    require(_value >= 0)

    def value: Int = _value
    def value_=(other: Int) {
        require(other >=0)
        _value = other
    }
}

客户端代码不会中断,因为 API 没有改变:

// use number...
val num = new MutableNumber(5)
num.value = 6
println(num.value)

我的问题是添加到 Scala-2.8 中的命名参数功能。如果我使用命名参数,我的 API发生变化并且破坏 api。

val num = new MutableNumber(value=5)  // old API
val num = new MutableNumber(_value=5) // new API

num.value = 6
println(num.value)

有什么优雅的解决方案吗?我应该如何设计我的 MutableNumber 类,以便以后可以在不破坏 API 的情况下添加约束?

谢谢!

4

3 回答 3

11

您可以使用与案例类相同的技巧:使用伴随对象。

object Example {
  class MutableNumber private (private var _value: Int) {
    require (_value >= 0)
    def value: Int = _value
    def value_=(i: Int) { require (i>=0); _value = i }
    override def toString = "mutable " + _value
  }
  object MutableNumber {
    def apply(value: Int = 0) = new MutableNumber(value)
  }
}

在这里它正在工作(并证明,在构造时,您必须使用该对象进行创建,因为构造函数被标记为私有):

scala> new Example.MutableNumber(5)
<console>:10: error: constructor MutableNumber cannot be accessed in object $iw
   new Example.MutableNumber(5)
   ^

scala> Example.MutableNumber(value = 2)
res0: Example.MutableNumber = mutable 2

scala> Example.MutableNumber()
res1: Example.MutableNumber = mutable 0
于 2010-11-11T20:38:14.313 回答
2
class MutableNumber {
    private var _value = 0 //needs to be initialized
    def value: Int = _value
    def value_=(other: Int) {
        require(other >=0) //this requirement was two times there
        _value = other
    }
}

您可以在花括号内修改任何类的所有成员

val n = new MutableNumber{value = 17}
于 2010-11-12T23:01:04.847 回答
2

感谢你的回答!顺便说一句,我认为 Scala 的人可能意识到存在一个问题:

Scala 2.8 的新特性:命名参数和默认参数

... 直到现在,参数的名称对于库开发人员来说是一个有点武断的选择,并且不被认为是 API 的重要部分。这突然改变了,因此如果参数 sep 在更高版本中重命名为分隔符,则对 mkString(sep = " ") 的方法调用将无法编译。

Scala 2.9 为这个问题提供了一个巧妙的解决方案,但是在我们等待的时候,请谨慎使用名称来引用参数,如果它们的名称将来可能会更改。

于 2010-11-11T22:50:39.927 回答