If we have a private field in a superclass, then as you would expect, it is listed in Type.fields() when called on a subclass. Example:
class Example {
private Str? myField
}
class Example2 : Example {
Void main() {
echo(typeof.fields) // --> [Example.myField]
}
}
But if the subclass has a field of the same name, then it hides the one defined in the parent:
class Example {
private Str? myField
}
class Example2 : Example {
private Str? myField
Void main() {
echo(typeof.fields) // --> [Example2.myField]
}
}
Whereas I would have expected: [Example.myField, Example2.myField]
As I'm not overriding myField, this is a bug right?
I came across this when using IoC and I wanted to inject the same private service (with the same name) into both the super and sub class.
brianSat 29 Nov 2014
No that isn't a bug, by design private fields are not inherited into subclasses. From docs:
Constructors are never inherited
Private slots are never inherited
Internal slots are inherited only by types within the same pod
All other slots are inherited
SlimerDudeSun 30 Nov 2014
Hi Brian, I think you're missing my point for your answer agrees with what I said! I'll try to be clearer...
class Example {
private Str? priField1
private Str? priField2
virtual public Str? pubField
}
class Example2 : Example {
private Str? priField1
override public Str? pubField
}
Type.fields() lists all the fields on that Type, both inherited and non-inherited, so Example2#.fields lists:
Example2.priField1 (obviously)
Example2.pubField because it overrides pubField from Example
Example.priField2 because it is on the parent
But I would have also expected Example.priField1 because it is not inherited or overriden in Example2
The private field Example2.priField1hidesExample.priField1 because they happen to have the same name.
Better Example
As far as reflection is concerned, it looks like private fields are inherited in some way. Take these classes:
An instance of Child should hold the the values 1, 2 and 3 and (despite them being private) I should be able to access them all using reflection. But it appears not to be the case:
Looking at fan.sys.JavaType.java and the initSlots() method, it seems that slots are mapped purely based on name, which is where the overriding / hiding behaviour comes from.
And I can confirm it's the same behaviour for methods too.
What are describing is exactly how it is designed to work. Slots are mapped by name only, so it is impossible to have to have two slots in your slots list with the same name. If anything the argument is whether private slots should show up in the reflection APIs, but in the end the current design was picked for compiler support so that reflection could be used during compile time checks. I played with a couple different designs, and ended up with the current one as the best set of trade-offs.
As a practical matter if what you are trying to do is reflect private slots and/or constructors, then you should really only use the declared fields of each type and walk the type inheritance list.
SlimerDudeSun 14 Dec 2014
Slots are mapped by name only
This is, of course, how the compiler works. But I would have imagined reflection to be a little more selective given that private slots should not be overridden.
reflection (is) used during compile time checks.
Oh, okay. Well, that explains everything then! As we're just talking about private slots, I concede that all this is a grey area anyway.
the argument is whether private slots should show up in the reflection APIs
I would vote for yes! It is really really handy, and if Fantom were to go down this route then selectively returning internal and protected slots would get rather tricky!
use the declared fields of each type and walk the type inheritance list.
Cheers. This is what I am now doing.
SlimerDudeWed 17 Dec 2014
Hi, I don't mean to bleat on, but is it possible to set overridden private fields by reflection? If so, which reflection methods do I need to use? I ask because I'm having trouble:
class Parent {
private Str? field
Void printParent() { echo(field) }
}
class Child : Parent {
private Str? field
Void printChild() { echo(field) }
}
child := Child()
Child# .fields.find { it.name == "field" }.set(child, "child")
Parent#.fields.find { it.name == "field" }.set(child, "parent")
child.printChild // --> parent, should be child
child.printParent // --> null, should be parent
brianThu 18 Dec 2014
Hey Steve,
I looked into this problem. Everything is correct all the way down the lowest levels of the Java runtime. The problem is that reflectively we call the setter which in Java is treated as an virtual override. That has always been a problem in Java in that we can't use true private fields without a ton of extra boiler plate code for closures, etc. And without using true private methods we have no support for invoking methods non-virtually.
For this specific problem, we can hack it for the common case where there isn't a user defined setter. But there is no suitable solution if those private fields had non-synthetic setters. I pushed a fix for that
SlimerDudeFri 19 Dec 2014
Hi Brian,
Thanks for looking into that, and for giving an explanation as to what's going on.
As all field setters / getters (and methods) map to non-private Java methods (which is the obvious way to go) the behaviour all makes sense.
SlimerDude Sat 29 Nov 2014
If we have a private field in a superclass, then as you would expect, it is listed in
Type.fields()
when called on a subclass. Example:But if the subclass has a field of the same name, then it hides the one defined in the parent:
Whereas I would have expected:
[Example.myField, Example2.myField]
As I'm not overriding
myField
, this is a bug right?I came across this when using IoC and I wanted to inject the same private service (with the same name) into both the super and sub class.
brian Sat 29 Nov 2014
No that isn't a bug, by design private fields are not inherited into subclasses. From docs:
SlimerDude Sun 30 Nov 2014
Hi Brian, I think you're missing my point for your answer agrees with what I said! I'll try to be clearer...
Type.fields()
lists all the fields on that Type, both inherited and non-inherited, soExample2#.fields
lists:Example2.priField1
(obviously)Example2.pubField
because it overridespubField
fromExample
Example.priField2
because it is on the parentBut I would have also expected
Example.priField1
because it is not inherited or overriden inExample2
The private field
Example2.priField1
hidesExample.priField1
because they happen to have the same name.Better Example
As far as reflection is concerned, it looks like private fields are inherited in some way. Take these classes:
An instance of
Child
should hold the the values1
,2
and3
and (despite them being private) I should be able to access them all using reflection. But it appears not to be the case:Looking at
fan.sys.JavaType.java
and theinitSlots()
method, it seems that slots are mapped purely based on name, which is where the overriding / hiding behaviour comes from.And I can confirm it's the same behaviour for methods too.
brian Sun 30 Nov 2014
What are describing is exactly how it is designed to work. Slots are mapped by name only, so it is impossible to have to have two slots in your slots list with the same name. If anything the argument is whether private slots should show up in the reflection APIs, but in the end the current design was picked for compiler support so that reflection could be used during compile time checks. I played with a couple different designs, and ended up with the current one as the best set of trade-offs.
As a practical matter if what you are trying to do is reflect private slots and/or constructors, then you should really only use the declared fields of each type and walk the type inheritance list.
SlimerDude Sun 14 Dec 2014
This is, of course, how the compiler works. But I would have imagined reflection to be a little more selective given that private slots should not be overridden.
Oh, okay. Well, that explains everything then! As we're just talking about
private
slots, I concede that all this is a grey area anyway.I would vote for yes! It is really really handy, and if Fantom were to go down this route then selectively returning
internal
andprotected
slots would get rather tricky!Cheers. This is what I am now doing.
SlimerDude Wed 17 Dec 2014
Hi, I don't mean to bleat on, but is it possible to set overridden private fields by reflection? If so, which reflection methods do I need to use? I ask because I'm having trouble:
brian Thu 18 Dec 2014
Hey Steve,
I looked into this problem. Everything is correct all the way down the lowest levels of the Java runtime. The problem is that reflectively we call the setter which in Java is treated as an virtual override. That has always been a problem in Java in that we can't use true private fields without a ton of extra boiler plate code for closures, etc. And without using true private methods we have no support for invoking methods non-virtually.
For this specific problem, we can hack it for the common case where there isn't a user defined setter. But there is no suitable solution if those private fields had non-synthetic setters. I pushed a fix for that
SlimerDude Fri 19 Dec 2014
Hi Brian,
Thanks for looking into that, and for giving an explanation as to what's going on.
As all field setters / getters (and methods) map to non-private Java methods (which is the obvious way to go) the behaviour all makes sense.