Основной вопрос Scala OOP - передать по ссылке? - PullRequest
18 голосов
/ 01 августа 2011

Я немного озадачен тем, насколько глупа эта проблема, и у меня серьезный тупик, но я подумал, что могу спросить в любом случае.

У меня есть объект Foo с несколькими полями. Я хочу метод, который может изменить любое из его полей в зависимости от того, какое из них передается в качестве параметра. Вот так:

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

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

Могу ли я так не использовать?

changeFooField(foo.x)

Если нет, как мне это сделать?

Ответы [ 4 ]

16 голосов
/ 01 августа 2011

Распространенная идиома - передать явный метод установки, который позаботится об изменении значения.

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 = _) - это простое временное закрытие, которое дает доступ на запись к foo.x.Нет необходимости добавлять этот API-интерфейс для самого Foo.

Так что, как выясняется, метод-установщик (foo.x= - или, скорее, foo.x_=) передается в качестве аргумента точно так же, как и любой другой.другой метод пройден.Вы должны помнить, что val и var в Scala на самом деле не определяют переменные, а создают методы, которые затем используются для доступа к реальным (скрытым) переменным.(val создает только метод получения, тогда как var, конечно, создает и метод получения, и установку).

13 голосов
/ 01 августа 2011

Нет, вы не можете. Вам нужно будет заключить значение поля в объект:

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"
7 голосов
/ 21 марта 2012

Вопреки всем ответам выше, это на самом деле вполне выполнимо в Scala без написания каких-либо специальных классов-оболочек.

Сначала вам нужно знать, что для любого не закрытого класса var, такого как используемыев исходном вопросе Scala автоматически генерирует геттеры и сеттеры.Поэтому, если у нас есть переменная с именем «color», Scala автоматически создает метод получения с одноименным названием «color» и метод установки с именем «color _ =».

Далее необходимо знать, что Scala позволяет получить ссылку на любойметод, вызывая специальный метод "_" для него (который требует места перед ним для устранения неоднозначности).

Наконец, собрав эти факты, вы можете легко получить безопасную для типов ссылку на любой метод получения / установки 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
2 голосов
/ 01 августа 2011

То, что вы хотите, не существует в Scala / Java. Самая близкая вещь была бы передачей функции установки.

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
...