Reputation: 15416
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?
Upvotes: 19
Views: 14726
Reputation: 11426
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
Upvotes: 8
Reputation: 67858
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).
Upvotes: 16
Reputation: 42047
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
Upvotes: 2
Reputation: 40461
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"
Upvotes: 14