3

I am trying to use private trait field in testing. Very simple example:

//Works fine with class A, but not trait A
trait A {  
  private val foo = "Some string"
}

class Test extends A {
  val field = classOf[A].getDeclaredField("foo")
  field.setAccessible(true)
  val str = field.get(this).asInstanceOf[String]
}

I got:

java.lang.NoSuchFieldException: foo at java.lang.Class.getDeclaredField

live example here

How to get this snippet executable?

4
  • As you can see from example, its also throwing an error Commented Oct 31, 2017 at 15:04
  • Ok. But how to avoid it? Commented Oct 31, 2017 at 15:12
  • Maybe this answer can help? Commented Oct 31, 2017 at 15:32
  • Not using reflection ... Commented Oct 31, 2017 at 17:12

2 Answers 2

6

A is a trait, which Scala translates to a JVM interface. Interfaces can't have fields, so there is no such field. The underlying field only gets added once the interface is actually mixed into a class.

So the first thing you would need to do get this is running is change classOf[A] to classOf[Test].

The second thing is to change getDeclaredField("foo") to .getDeclaredField("A$$foo").

Sign up to request clarification or add additional context in comments.

4 Comments

Seth you got it! how did you know that the value to ask as string was "A$$foo"?
I inspected the output of javap -private Test. Note that the details of how traits are encoded may vary from Scala version to Scala version.
you rules! really, I added it to my original answer, but quoting you, I hope you get the correct credit for that, I was a long time trying to figure out that, I also looked out for the output, genius ;)
Just a small point. In real-world projects all classes belongs to some packages. So i have to write .getDeclaredField("com.example.A$$foo")
4

Edit special thanks to @Seth Tisue (up votes and accept to him please)

class Test extends A {
  val field:Field = this.getClass.getDeclaredField("A$$foo")

  field.setAccessible(true)

  println(field.get(this).asInstanceOf[String])

}

"A$$foo" is the correct way to get the super type attribute, this and use this.getClass. I didn't know it before, but with that correction, your code will work just great!

First Idea:

trait A {
  private val foo = "Some string"
}


class Test extends A {
  val fields: Seq[Field] = this.getClass.getDeclaredFields.toList

  val field = fields.filter(x => {
    println(x.getName)
    x.getName.contains("foo")
  }).head

  field.setAccessible(true)

  println(field.get(this).asInstanceOf[String])

}

As you can see, when you print the name of the "foo" variable, is not really "foo", it's something else:

A$A295$A$A295$A$$foo

in my case, and that's why you (and I) got the error

java.lang.NoSuchFieldException: foo at java.lang.Class.getDeclaredField

So, my idea for now, I hope someone came with a better one, is look if "foo" is inside the variable name "A$A295$A$A295$A$$foo" so you can tell thats the variable you where looking for.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.