Dominic Bou-Samra
Dominic Bou-Samra

Reputation: 15416

Basic Scala OOP question - pass by reference?

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

Answers (4)

Magnus
Magnus

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

Debilski
Debilski

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

Kim Stebel
Kim Stebel

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

paradigmatic
paradigmatic

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

Related Questions