Конструкторы Scala, именованные аргументы и неявные методы получения / установки - PullRequest
10 голосов
/ 21 февраля 2011

Можно ли использовать именованные аргументы в конструкторе Scala, а затем переопределить методы получения и установки, не нарушая интерфейс конструктора и не делая код крайне уродливым?

Взять следующий бит кода Scala

class Person( var FirstName: String, var LastName: String )

Красиво и чисто.Это создаст простой класс с именем person, который мы могли бы использовать следующим образом:

val john = new Person( FirstName="John", LastName="Doe" )
john.FirstName = "Joe"
println( john.FirstName )

Позже мы решаем, что мы хотим добавить некоторую проверку в установщик FirstName.Таким образом, мы создаем новую приватную локальную переменную и переопределяем методы getter и setter

class Person( var _FirstName: String, var _LastName: String ) {

    def FirstName = _FirstName  
    def FirstName_= (value:String) = _FirstName = value

}

Все еще несколько чисто, однако для этого нам пришлось изменить имена аргументов конструктора, таким образом нарушаявнешний интерфейс.

Первое решение этой проблемы, с которым я столкнулся, было

class Person {
    var _FirstName:String = null 
    var LastName:String  = null

    def FirstName = _FirstName  
    def FirstName_= (value:String) = _FirstName = value

    def this( FirstName: String, LastName: String ){
        this()
        this._FirstName = FirstName
        this.LastName = LastName 
    }

}

Это несколько уродливо и не элегантно и устраняет большинство веских причин, по которым я использовал scala.

Есть ли лучший способ сделать это?

tl; dr Как переопределить методы получения / установки для элементов, определенных в конструкторе по умолчанию, без уродливости кода или изменения открытого интерфейса?

Ответы [ 3 ]

10 голосов
/ 21 февраля 2011

Рассматривали ли вы использование объекта-компаньона?

class Person private (f: String, l: String ) {
   var FirstName = f
   var LastName = l
}

object Person {
   def apply(FirstName:String, LastName:String) = 
       new Person(FirstName, LastName) 
}
5 голосов
/ 21 февраля 2011

Если вы еще не используете неявные преобразования для создания аргументов, вы можете сделать что-то вроде этого:

def validateName(s: String) = {
  if (s.length>0 && s(0).isUpper) s
  else throw new IllegalArgumentException(s+" is not a name!")
}

object Example {
  private[Example] class ValidatedName(val s: String) { }
  class Person(var firstName: ValidatedName, var lastName: String) { }
  implicit def string2valid(s: String) = new ValidatedName(validateName(s))
  implicit def valid2string(v: ValidatedName) = v.s
}

scala> new Example.Person("Joe","Schmoe")
res17: Example.Person = Example$Person@51887dd5

scala> new Example.Person("ee","cummings")
java.lang.IllegalArgumentException: ee is not a name!

Он не двоичный совместимый, но он совместим с исходным кодом (опять же, если имена уже не полагались на неявные преобразования).

Другая немного более длинная возможность - создать предка-невидимки:

class CheckedPerson(private var first: String, var lastName: String) {
  def firstName = first
  def firstName_=(s: String) { first = validateName(s) }
}
class Person(firstName: String, lastName: String) extends
  CheckedPerson(validateName(firstName),lastName) { }

, для которого я не уверен в бинарной совместимости, но определенно даст совместимость с исходным кодом.

4 голосов
/ 21 февраля 2011

Нет. В настоящее время нет способа сделать это, в настоящее время это не предмет исследований.

Это одна из моих любимых мозгов, связанных с языком: нет разумного способа объединить аргументы конструктора и самоопределяемые методы получения / установки.

Если вы недовольны функциональностью, предоставляемой class Person( var FirstName: String, var LastName: String ), это в основном означает «вернуться к многословности Java».

...