Scala Свойства Вопрос

Я все еще изучаю Scala, но одна вещь, которую я считал интересной, заключается в том, что Scala стирает линию между методами и полями. Например, я могу построить такой класс...

class MutableNumber(var value: Int)

Ключевым моментом здесь является то, что переменная var в аргументе конструктора автоматически позволяет мне использовать поле "значение", например, getter/setter в java.

// 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. Если я использую named-parameters, мой 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?

Спасибо!

Ответ 1

Вы можете использовать тот же трюк, что и классы case: используйте сопутствующий объект.

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

Ответ 2

Спасибо за ответ! В стороне, я думаю, что Scala -guys может знать, что есть проблема:

Что нового в Scala 2.8: Именованные и стандартные параметры

... До сих пор имена аргументов были несколько произвольным выбором для разработчиков библиотек и не считались важной частью API. Это внезапно изменилось, так что вызов метода mkString (sep = "") не сможет скомпилироваться, если аргумент sep был переименован в разделитель в более поздней версии.

Scala 2.9 реализует аккуратное решение этой проблемы, но пока мы этого ждем, будьте осторожны в отношении аргументов по имени, если их имена могут измениться в будущем.

Ответ 3

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}