0

I don't know why but the class below doesn't work if instance variable text is private, but if I leave out private, it works.

Debugging the test in section "setField" I could see that the instance variable name should be "text" but it becomes "com$test$SimpleTest$$text"

package com.test
import org.testng.annotations.Test
import org.springframework.test.util.ReflectionTestUtils

class SimpleTest {
  private var text = ""

  @Test
  def testValueOfX(): Unit = {
    val simpleTest = new SimpleTest
    ReflectionTestUtils.setField(simpleTest,"text", "abc")

    println(
      Option[String](null)
        .map(v => v + " 123")
        .getOrElse {
          simpleTest.text + " 321"
    })
  }
}

I believe that the problem someway be the "getOrElse" because if I leave out too, it works.

Lira
  • 45
  • 1
  • 6
  • What do you mean by "doesn't work"? Which line throws what error? How does removing `getOrElse` make it work? – Michael Zajac Feb 02 '15 at 00:49
  • This classe doesn't work if you try run it. The line throws `ReflectionTestUtils.setField(simpleTest,"text", "abc")` . If I put out instance variable `simpleTest.text` of the anonymous block, it works. – Lira Feb 02 '15 at 15:57

2 Answers2

2

Scala compiler has a right to compile your private field into an any working java code, as it doesn't affect interoperability (if you don't do any tricks). Spring's setField actually do such trick, as it makes your private field accessible (setAccessible(true) inside). Public fields are always compiling as is to give you appropriate interface from Java.

Use http://docs.scala-lang.org/overviews/reflection/environment-universes-mirrors.html to work with Scala reflection. Also this article may be helpful.

Here is explanation why scalac uses another name for private field.

P.S. The reason why removing .getOrElse(text) make it work is because you don't use text anywhere but inside this piece of code.

Community
  • 1
  • 1
dk14
  • 22,206
  • 4
  • 51
  • 88
  • About `.getOrElse` actually if I put it out anonymous block, it works. The problem is when a instance variable is inside anonymous block. But I'm reading the articles. – Lira Feb 02 '15 at 02:18
  • anyway - compiler can do whatever it wants with your private field and this behaviour may change between versions of Scala, so you can't use java reflection - only scala reflectionn. – dk14 Feb 02 '15 at 03:14
  • about experiment itself - I suspect it's not about block - `println(simpleTest.text)` also shouldn't work - or any actual usage. Because if you're just have putten `simpleTest.text` somewhere between lines - compiler knows that value is unused (actually it prints warning about that). Anyway, it's `scalac`'s internals and it's not a bug, so we can't change it. – dk14 Feb 02 '15 at 03:20
0

This class is only to show the problem, actually it is very different of real class. So I changed the strategy to receive an instance by @Autowired a method instead @Autowired a field.

package com.test
import org.testng.annotations.Test
import org.springframework.test.util.ReflectionTestUtils

class SimpleTest {
  // item 1
  private var text = ""

  @Test
  def testValueOfX(): Unit = {
    val simpleTest = new SimpleTest
    ReflectionTestUtils.invokeSetterMethod(simpleTest, "text", "abc")

    println(
          Option[String](null)
        .map(v => v + " 123")
        .getOrElse {
        simpleTest.text + " 321"
      })
  }
  // item 2
  def setText(text: String): Unit = {
    this.text = text
  }
}

I'm not using @Autowired in this sample but in the real class I am. So if you need to get an instance follow instructions below.

Item 1 -> if I put on @Autowired it doesn't work because like said dk14 Scala compiler has a right to compile your private field into an any working java code. So, the compiler change the field's name when it compiles the class

Item 2 -> I put on @Autowired in the setter method, it works.

Lira
  • 45
  • 1
  • 6