Reputation: 78421
In Scala, is it possible to use reflection to access the outer class of an inner class? For example:
class A {
val inner = new {
println(getClass.getConstructors.toList)
println(getClass.getDeclaredFields.toList)
}
}
scala> val a = new A
List(public $line11.$read$$iw$$iw$A$$anon$1($line11.$read$$iw$$iw$A))
List()
a: A = A@45f76fc7
I think that the Scala compiler saves a reference to the outer class somewhere, but you can see here that the list of fields printed in the constructor is empty. Also, it looks like the constructor takes a reference to the outer class instance (but it's hard to tell for sure --- I'm not sure exactly what's going on here). I have also noticed in some cases there is an $outer
field that seems to be what I need, but it it's not always there, and I don't understand this.
WHY???!!! I have an inner class that I need to create a new instance of using reflection. The new instance is being copied from an existing instance, and needs to have the same outer reference.
Upvotes: 5
Views: 2098
Reputation: 40703
Inner classes MUST have a reference to their outer class. This is done implicitly by the JVM. In the following example the call to new InnerClass()
is translated as new InnerClass(this)
. The instance of OuterClass
is stored in the field $outer
in the inner class which can be accessed using this syntax OuterClass.this
.
class OuterClass {
val str = "abcde";
val inner = new InnerClass();
class InnerClass {
def printStr() {
println(OuterClass.this.str);
}
}
}
To construct a new instance of InnerClass via reflections you must have a reference to an instance of OuterClass. To create the new instance do the following.
val outer = new OuterClass;
val newInner = outer.inner.getClass.getDeclaredConstructor(classOf[OuterClass])
.newInstance(outer);
As an addendum, I would say that having to create a new inner class via reflections sounds like pretty poor coding practice. Only OuterClass should ever see instances of InnerClass and so should be prepared for any situation where it may need to create a new instance of InnerClass. If other classes have visibility of InnerClass then it needs to be refactored into its own class.
Upvotes: 4
Reputation: 3906
I don't think you can reliably get the parent unless you use the parent outside the constructor if the inner class. Given:
class X {
val str = "xxyy"
val self = this
val inner = new {
override def toString():String = {
"inner " + self.toString
}
}
override def toString():String = {
"ima x" + this.str
}
}
If you do javap you get a private field $outer
but given:
class X {
val str = "xxyy"
val self = this
val inner = new {
println(self)
}
override def toString():String = {
"ima x" + this.str
}
}
The javap does not indicate the $outer field.
Upvotes: 5
Reputation: 49705
If you know you'll need this, then it's better (by far) to make the reference explicit and avoid reflection entirely:
class A {
outerA =>
val inner = new {
val outer = outerA
println(getClass.getConstructors.toList)
println(getClass.getDeclaredFields.toList)
}
}
The magic sauce here is the self type outerA =>
allowing you to give a unique name to the this
reference.
Upvotes: 5