基本的Scala OOP问题 - 通过引用传递?

Dom*_*mra 18 oop scala

我有点难过这个问题是多么愚蠢而且有严重的思维空白,但我想我还是会问.

我有一个对象Foo,有几个字段.我想要一个可以更改其任何字段的方法,具体取决于作为参数传入的字段.像这样:

class Foo {
    var x = 0
    var y = 0
}

class Bar {
    def changeFooField(field : Int) = {
        field = 1        
    }
}
Run Code Online (Sandbox Code Playgroud)

我不能这样使用吗?:

changeFooField(foo.x)
Run Code Online (Sandbox Code Playgroud)

如果没有,我该如何做到这一点?

Deb*_*ski 16

一个常见的习惯用法是传递一个显式的setter方法,该方法负责改变值.

class Foo {
  var x = 0
  var y = 0
}

class Bar {
  def changeFooField(setter: (Int) => Unit) = {
    setter(1)
  }
}

val foo = new Foo
val bar = new Bar
bar.changeFooField(foo.x = _) // or equivalent: bar.changeFooField((i: Int) => foo.x = i)

assert(foo.x == 1)
Run Code Online (Sandbox Code Playgroud)

(foo.x = _)是一个简单的临时闭包,允许写访问foo.x.没有必要将此setter API添加到Foo自身.

因此,事实证明,setter方法(foo.x=或者更确切地说foo.x_=)作为参数传递,与传递任何其他方法完全相同.你必须记住这一点,val并且var在Scala中实际上并没有指定变量,而是创建了用于访问实际(隐藏)变量的方法.(val只创建一个getter方法,var当然创建getter和setter).


par*_*tic 13

你不能.您需要将字段值包含在对象中:

class Field[T]( var value: T )

class Foo {
  val x = new Field(0)
  val y = new Field(0)
}

class Bar {
  def changeFooField( field: Field[Int] ) {
    field.value = 1
  } 
}

val f = new Foo
(new Bar).changeFooField( f.x )
println( f.x.value + " / " + f.y.value ) // prints "1 / 0"
Run Code Online (Sandbox Code Playgroud)

  • @Dominic没有明确提到的基本问题是`Int`s是不可变的.如果您的字段类型是可变的,如上例所示,则很容易 (7认同)

Mag*_*nus 7

与上面的所有答案相反,这实际上在Scala中非常可行而没有编写任何特殊的包装类.

首先,您需要知道对于任何非私有类var,例如原始问题中使用的那些,Scala会自动生成getter和setter.因此,如果我们有一个名为"color"的var,Scala会自动创建一个名为"color"的getter和一个名为"color_ ="的setter.

接下来,您需要知道Scala允许您通过在其上调用特殊的"_"方法来获取对任何方法的引用(在消除歧义之前需要一个空格).

最后将这些事实放在一起,您可以轻松获得任何var的getter/setter的类型安全引用,并使用该引用动态设置/获取该var的值:

class Foo {
    var x = 0
}

object Foo {
    def setField[T](setter: T => Unit, value: T) {setter(value)}
    def getField[T](getter: () => T ) = {getter()}
}

val f = new Foo
val xsetter = f.x_= _
val xgetter = f.x _

Foo.setField(xsetter, 3)
println(f.x) //prints 3
println(Foo.getField(xgetter)) //prints 3
Run Code Online (Sandbox Code Playgroud)