开发者

Scala Properties Question

I'm still learning Scala, but one thing I thought was interesting is that Scala blurs the line between methods and fields. For instance, I can build a class like this...

class MutableNumber(var value: Int)

The key here is that the var in the constructor-argument automatically allows me to use the 'value' field like a getter/setter in java.

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

If I want to add constraints, I can do so by switching to using methods in place of the instance-fields:

// 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
    }
}

The client side code doesn't break since the API doesn't change:

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

My hang-up is with the named-parameter feature that was added to Scala-2.8. If I use named-parameters, my API does change and it does break the api.

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

num.value = 6
println(num.value)

Is there any elegant solution to this? How should I design my MutableNumber class so that I ca开发者_运维知识库n add constraints later on without breaking the API?

Thanks!


You can use the same trick that case classes do: use a companion object.

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)
  }
}

And here it is working (and demonstrating that, as constructed, you must use the object for creations, since the constructor is marked private):

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


Thanks for the answer! As an aside, I think the Scala-guys might be aware that there's an issue:

What's New in Scala 2.8: Named and Default Parameters

... Until now, the names of arguments were a somewhat arbitrary choice for library developers, and weren't considered an important part of the API. This has suddenly changed, so that a method call to mkString(sep = " ") will fail to compile if the argument sep were renamed to separator in a later version.

Scala 2.9 implements a neat solution to this problem, but while we're waiting for that, be cautious about referring to arguments by name if their names may change in the future.

  • http://www.artima.com/scalazine/articles/named_and_default_parameters_in_scala.html


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
    }
}

you can modify all members of any class within curly braces

val n = new MutableNumber{value = 17}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜