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}
精彩评论