【问题标题】:Scala Constructors, Named Arguments, and Implicit Getters/SettersScala 构造函数、命名参数和隐式 Getter/Setter
【发布时间】:2011-02-21 13:43:23
【问题描述】:

是否可以在 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 设置器添加一些验证。因此,我们创建一个新的私有局部变量并覆盖 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 而不使代码丑陋或更改公共接口?

【问题讨论】:

  • 我不确定你的例子是真实的还是为了讨论而编造的,但为什么有头脑正常的人会使用 @987654325 向世界敞开心扉@?
  • 这是在我第一次学习 Scala 的早期。绝对关于这个问题的一切以及我试图做的事情都是愚蠢的。

标签: scala constructor getter-setter coding-style


【解决方案1】:

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

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

【讨论】:

  • 这个二进制文件是否兼容旧版本?
  • @ziggystar,Person(FirstName = "foo", LastName = "bar") 语法将是二进制兼容的,但 new Person(...) 将不再有效。
  • 不幸的是,我需要保持构造函数公共接口不变。
【解决方案2】:

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

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

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

【讨论】:

  • 隐身祖先方法看起来是最好的方法。仍然不是特别优雅,但比我的第一次尝试要干净得多。谢谢:)
  • 你不能把验证逻辑放在ValidatedName的主构造函数中而不是单独的函数中吗?
  • @ErikAllik 不;我认为这会导致在对象构造期间运行验证,而不是在设置字段值期间运行。因此它可以防止创建“ee cummings”,但您可以在之后将人名更改为“ee cummings”,这会很糟糕(假设您希望所有 Person 对象都具有大写名称)。
【解决方案3】:

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

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

如果您对 class Person( var FirstName: String, var LastName: String ) 提供的功能不满意,它基本上意味着“回到 Java 的冗长”。

【讨论】:

  • 您知道社区中是否普遍接受对语法的更改?我会考虑自己加入并实施一些更改,以使 getter/setter 更好地工作。
  • Afaik,您首先必须在“SID”(Scala 改进文档)中写下您的计划,并且 Mrtin Odersky 必须同意。 :-) 恕我直言,我认为改进的机会非常渺茫。
猜你喜欢
  • 2013-07-30
  • 1970-01-01
  • 2017-03-05
  • 2011-07-10
  • 1970-01-01
  • 2023-03-06
  • 2014-01-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多