19

I'm a little stumped at how silly this problem is and have a serious mindblank, but I thought I might ask anyway.

I have an object Foo, with several fields. I want a method that can change any of its fields depending on which one is passed in as a parameter. Like so:

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

class Bar {
    def changeFooField(field : Int) = {
        field = 1        
    }
}

Can I not use it like so?:

changeFooField(foo.x)

If not, how do I accomplish this?

Michael Lorton
  • 43,060
  • 26
  • 103
  • 144
Dominic Bou-Samra
  • 14,799
  • 26
  • 100
  • 156

4 Answers4

16

A common idiom is to pass an explicit setter method which takes care of changing the value.

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)

(foo.x = _) is a simple ad-hoc closure which allows write access to foo.x. There is no need in adding this setter API to Foo itself.

So, as it turns out, the setter method (foo.x= – or rather foo.x_=) is passed as an argument exactly as any other method is passed. You’ll have to remember that val and var in Scala don’t actually specify variables but create the methods which are then used to access the real (hidden) variables. (val only creates a getter method, whereas var of course creates both getter and setter).

Debilski
  • 66,976
  • 12
  • 110
  • 133
14

No you cannot. You'll need to enclose the field value into an object:

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"
paradigmatic
  • 40,153
  • 18
  • 88
  • 147
  • Okay thanks. Yeah thought it wasn't possible without creating a new object wrapping the field. – Dominic Bou-Samra Aug 01 '11 at 09:11
  • 8
    @Dominic The basic problem, which hasn't explicitly been mentioned, is that `Int`s are immutable. If your field types are mutable, as in the example above, it's easy – Luigi Plinge Aug 01 '11 at 16:57
8

Contrary to all the answers above, this in fact is quite doable in Scala without writing any special wrapper classes.

First you need to know that for any non-private class var, such as the ones used in the original question, Scala automatically generates getters and setters. So if we have a var called "color", Scala automatically creates a getter eponymously called "color" and a setter called "color_=".

Next you need to know that Scala lets you obtain a reference to any method by calling the special "_" method on it (which requires a space before it for disambiguation).

Finally putting these facts together, you can easily get a type-safe reference to any var's getter/setter and use that reference to dynamically set/get that var's value:

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
Magnus
  • 10,736
  • 5
  • 44
  • 57
2

What you want doesn't exist in Scala/Java. The closest thing would be passing around a setter function.

scala> class Foo {
     | var x = 0
     | var y = 0
     | val xSetter = (i:Int) => x = i
     | val ySetter = (i:Int) => y = i
     | }
defined class Foo

scala> def setField(setter:Int=>Unit) = setter(1)
setField: (setter: (Int) => Unit)Unit

scala> val f = new Foo
f: Foo = Foo@eb203b

scala> f.x
res0: Int = 0

scala> setField(f.xSetter)

scala> f.x
res3: Int = 1
Kim Stebel
  • 41,826
  • 12
  • 125
  • 142
  • +1 instead of defining a `setField`, I can just apply `xSetter`, like `f.xSetter(1)`. right? to have the same effect? – Nishant Aug 01 '11 at 09:30
  • 2
    @Nishant but in that case you could also do `f.x = 1`… – Debilski Aug 01 '11 at 09:33
  • @Debilski oh, right. :) so I am not sure why it's done so indirectly? I mean is there a pattern like this, in Scala? – Nishant Aug 01 '11 at 09:50
  • ah, nevermind. Pass by reference is what is OP wanted to be explained. – Nishant Aug 01 '11 at 09:59
  • It is done indirectly, because you cannot pass a `var` field itself in Scala. So, if you want to make the change inside some other method, you’ll either have to pass the class itself or a specific setter for the `var`. – Debilski Aug 01 '11 at 10:02