Scala构造函数、命名参数和隐式getter/setter

Scala构造函数、命名参数和隐式getter/setter,scala,constructor,getter-setter,coding-style,Scala,Constructor,Getter Setter,Coding Style,是否可以在Scala构造函数中使用命名参数,然后在不破坏构造函数接口或使代码非常难看的情况下重写getter和setter 以下面的scala代码为例 class Person( var FirstName: String, var LastName: String ) 又好又干净。这将创建一个名为person的简单类,我们可以按以下方式使用它 val john = new Person( FirstName="John", LastName="Doe" ) john.FirstName =

是否可以在Scala构造函数中使用命名参数,然后在不破坏构造函数接口或使代码非常难看的情况下重写getter和setter

以下面的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 setter添加一些验证。因此,我们创建一个新的私有局部变量并重写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如何覆盖默认构造函数中定义的成员的getter/setter而不使代码变得丑陋或更改公共接口?

否。目前没有办法做到这一点,这不是研究的重点

这是我对该语言的主要不满之一:没有合理的方法将构造函数参数和自定义getter/setter方法结合起来


如果您不满意函数类人(var FistNoN:String,var LaSTNEST:String)< /C> >,它基本上意味着“返回java的ValueSt.”

您是否考虑使用一个伴随对象?

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

如果尚未使用隐式转换创建参数,则可以执行以下操作:

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

对此,我不确定二进制兼容性,但肯定会提供源代码兼容性。

此二进制文件与旧版本兼容吗?@ziggystar,
Person(FirstName=“foo”,LastName=“bar”)
语法将与二进制文件兼容,但
new Person(…)
将不再工作。不幸的是,我需要保持构造函数公共接口不变。您知道社区是否接受对语法的更改吗?我会考虑跳槽并实现一些改变,使吸气剂/设定器工作得更好一点。首先,你必须在“SID”(Scala改进文档)中写下你的计划,Mrtin Odersky必须同意。我认为改进的可能性很小。隐形祖先法看起来是最好的方法。仍然不是特别优雅,但比我第一次尝试要干净得多。谢谢:)你不能把验证逻辑放在
ValidatedName
的主构造函数中,而不是放在一个单独的函数中吗?@ErikAllik No;我认为这将导致验证在对象构造期间运行,而不是在字段值设置期间运行。因此,它可以防止创建“ee-cummings”,但您可以在之后将此人的姓名更改为“ee-cummings”,这将是不好的(因为您希望所有person对象都具有大写名称)。我不确定您的示例是否真实或是为了讨论而虚构的,但是为什么头脑正常的人会用
public var
向全世界开放一个类的胆识呢?这是在我第一次学习Scala的早期。关于这个问题和我想做的一切都是愚蠢的。