开发者

Reflection does not return a correct instance

I have wired experience with reflections. At first some sample code:

public abstract class A {

    public A () {
        init();
    }

    public abstract void init ();
}


public class B extends A {

    private int i = 0;

    public B () {
        super();
        System.out.println(i);
    }

    public void init () {
        i = 1;
    }
}

Somewhere in my code I use the reflection api to instantiate an object B.

Class<AbstractSection> bc = (Class<AbstractSection>) Class.forName(B);
Constructor<?> bcon = bc.getConstructor();
B b = (B) bcon.newInstance();

What I expected was an instance of B with the variable i set to value '1开发者_开发百科'. What I got is an instance of B with i still set to '0'. With a closer look with a debugger I saw that this is not exactly correct: i is NOT still set to '0'. It is changed to '1' in the init() method and set back to '0' in the very moment when the super() call returns.

Anyone a clue? Thanks in advance,

manuel

PS: I know I can solve this by calling init() not in the super class but in the inheriting constructor.


For starters, this is nothing to do with reflection - you'd get the same results if you instantiate the class yourself.

Your confusion likely stems from the way that the i field is defined - it looks like it's set to 0 as soon as it "exists". In reality, the assignment to zero is one of the first lines of your constructor (though, crucially, after the call to super() as is required by constructors in general).

In other words, your class is exactly equivalent to the following:

public class B extends A {

    private int i;

    public B () {
        super();
        i = 0;
        System.out.println(i);
    }

    public void init () {
        i = 1;
    }
}

I presume you can see now why the output is 0 instead of 1 - because the call to init() happens before the field is initialised to 0.


It is for this, and other reasons, that on the whole you should avoid calling subclass methods from a superclass constructor - since the subclass won't even have been initialised at this point, so invariants could easily be violated. (Calling methods on an unconstructed object is always a very bad idea!) That's the root cause of your problem, and the direction that you should be looking to address with a fix.

Constructors should constrain themselves to only calling methods that are private or final, for this reason. For more details, see (amongst others):

  • What's wrong with overrideable method calls in constructors?
  • Problem in instance variable initialization
  • State of Derived class object when Base class constructor calls overridden method in Java
  • Don’t call subclass methods from a superclass constructor


Any non-static class properties will be set to their default value in class's constructor:

boolean                          false
char                             '\u0000'
byte,short,int,long              0
float, double                    +0.0f or +0.0d
object                           null


There will be no difference if you call constructor instead of using reflection.

Declarations like

private int i = 0;

become part of every declared constructor right after call of super. So init is called before assignment to 0.


As the other answers state the method (generated by the compiler and initializes your fields) is called after super(), so it goes like that:

super()
    init() // i = 1
this()
    <init> // i = 0

I would like to add that you should not call a method in your constructor which may be overriden by your clients. This may cause unpredictable "fuckup" ;)

More details in effective java, can't find the item rigth now.


Because the order of execution is as follows: constructor of the subclass calls the superconstructor, that superconstructor sets i = 0, initialization of fields in the subclass then sets i = 1, and finally is executed the rest of the constructor of the subclass.

This follows the general rule: an object is initialized first by a superclass, and then by a subclass. This is why, if you'd try to call a non--static method within super()'s arguments, you'd get an error "cannot reference this before supertype constructor has been called". If you'd try to modify a field in super(), you'd get another error message. This prevents initialization of the subclass before the superconstructor initialized it.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜