【问题标题】:Scala Properties QuestionScala 属性问题
【发布时间】:2011-05-08 17:12:47
【问题描述】:

我仍在学习 Scala,但我认为有趣的一件事是 Scala 模糊了方法和字段之间的界限。例如,我可以构建一个这样的类...

class MutableNumber(var value: Int)

这里的关键是构造函数参数中的 var 自动允许我像 java 中的 getter/setter 一样使用“值”字段。

// 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 的命名参数功能感到不满。如果我使用命名参数,我的 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 的情况下添加约束?

谢谢!

【问题讨论】:

    标签: scala properties scala-2.8 javabeans named-parameters


    【解决方案1】:

    您可以使用与案例类相同的技巧:使用伴随对象。

    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
    

    【讨论】:

    • 有趣!因此,通过隐藏构造函数,我迫使每个人都使用伴生对象。如果我想让 MutableInteger 本身成为一个案例类怎么办?我知道,如果我只是在类定义前面加上“case”,Scala 会自动为我创建伴生对象……这个解决方案还能用吗?
    • 是的,如果您为案例类明确定义了伴随对象,则该对象的成员将合并到生成的对象中(并且可以覆盖,例如,如果您想提供调整后的 @ 987654323@ 方法,但保留自动生成的unapply)。
    • @shj - 不,这是行不通的,因为案例类假定直接(未保护)访问构造函数变量。你这样做是因为你想要守卫(在这种情况下以require(_value &gt;= 0) 的形式)。
    • scala 模式匹配是否需要直接访问构造函数?我认为 Scala 添加伴随对象的原因之一是您不需要显式使用构造函数。我不应该在案例类中使用警卫吗?
    • @David - 你如何覆盖自动生成的 apply() 方法?当我尝试这样做时,我得到“错误:方法应用被定义了两次”。
    【解决方案2】:

    感谢您的回答!顺便说一句,我认为 Scala 的人可能已经意识到存在问题:

    Scala 2.8 的新特性:命名参数和默认参数

    ... 到目前为止,参数的名称对于库开发人员来说是一个有点武断的选择,并且不被认为是 API 的重要部分。这突然发生了变化,因此如果参数 sep 在以后的版本中被重命名为分隔符,对 mkString(sep = " ") 的方法调用将无法编译。

    Scala 2.9 为这个问题提供了一个巧妙的解决方案,但是在我们等待的时候,如果它们的名称将来可能会更改,请谨慎使用名称来引用参数。

    【讨论】:

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

    【讨论】:

    • 这确实有为每个使用大括号的 MutableNumber 实例创建匿名的缺点。
    猜你喜欢
    • 2011-06-21
    • 2016-05-17
    • 1970-01-01
    • 1970-01-01
    • 2021-07-19
    • 2010-10-18
    • 1970-01-01
    • 2017-07-21
    • 2016-05-07
    相关资源
    最近更新 更多