Scala 构造函数、命名参数和隐式 Getter/Setter

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5066684/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 02:51:10  来源:igfitidea点击:

Scala Constructors, Named Arguments, and Implicit Getters/Setters

scalaconstructorgetter-settercoding-style

提问by James Davies

Is it possible to use named arguments in a Scala constructor, and later on override getters and setters without breaking the constructor interface or making the code extremely ugly?

是否可以在 Scala 构造函数中使用命名参数,然后在不破坏构造函数接口或使代码变得非常丑陋的情况下覆盖 getter 和 setter?

Take the following bit of scala code

拿下面的scala代码

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

Nice and clean. This would create a simple class called person, which we could use in the following way

漂亮干净。这将创建一个名为 person 的简单类,我们可以通过以下方式使用它

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

Later, we decide we want to add some validation to the FirstName setter. As such, we create a new private local variable and override the getter and setter methods

稍后,我们决定要向 FirstName setter 添加一些验证。因此,我们创建一个新的私有局部变量并覆盖 getter 和 setter 方法

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

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

}

Still somewhat clean, however in order to do this, we've had to change the constructor argument names, thus breaking the external interface.

仍然有些干净,但是为了做到这一点,我们不得不更改构造函数参数名称,从而破坏了外部接口。

The first solution to this problem I came up with was

我想出的这个问题的第一个解决方案是

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 
    }

}

Which is somewhat ugly and inelegant, and removes most of the nice reasons I was using scala in the first place.

这有点丑陋和不优雅,并且消除了我首先使用 Scala 的大部分好理由。

Is there a better way of doing this?

有没有更好的方法来做到这一点?

tl;dr How to override getters/setters for members defined in the default constructor without making the code ugly or changing the public interface?

tl;dr 如何在不使代码丑陋或更改公共接口的情况下覆盖默认构造函数中定义的成员的 getter/setter?

采纳答案by Rex Kerr

If you're not already using implicit conversions to create the arguments, you can do something like this:

如果您还没有使用隐式转换来创建参数,您可以执行以下操作:

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!

It's not binarycompatible, but it is source compatible (again, if the names weren't already relying upon implicit conversions).

它不是二进制兼容的,但它是源兼容的(同样,如果名称尚未依赖于隐式转换)。

Another slightly longer possibility is to create a stealth ancestor:

另一种稍长的可能性是创建一个隐形祖先:

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

for which I'm not sure about binary compatibility, but will definitely give source compatibility.

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

回答by Landei

Did you consider using an companion object?

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

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

回答by soc

No. There is currently no way to do that, it's currently not the focus of research.

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

It is one of my major pet peeves I have with the language: There is no sensible way to combine constructor arguments and self-defined getter/setter methods.

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

If you're not happy with the functionality class Person( var FirstName: String, var LastName: String )provides, it basically means "back to Java's verboseness".

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