Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reflection does not return a correct instance

Tags:

java

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.

like image 400
Manuel Avatar asked Aug 07 '26 14:08

Manuel


2 Answers

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
like image 188
Andrzej Doyle Avatar answered Aug 11 '26 12:08

Andrzej Doyle


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
like image 26
Foredoomed Avatar answered Aug 11 '26 11:08

Foredoomed