1

So I have two projects A and B, and project B is imported in project A, and in project B I want to initialize some objects which have static initializers. The problem is, they aren't getting called (already tested with final keyword, does not help).

So I actually want to have a small system and it should go this way (every class decribed here are in project B):

  • class A is a main class in which you can call a method addClassToLoad()* to add other classes (which will be "loaded" when method start() in class A will be called);
  • classes B, C and D call method addClassToLoad() from its static initializer;
  • when some class from project A calls a method start(), class A lists all classes it has gotten and calls a method onLoad() (explained in *).

And every method is static, so it's meant to be only one (no "instancing").

Saddly, static initializers aren't getting called.

And the question is: do I do something wrong (or maybe it is not possible at all) or maybe there is another way to do this small system? (I just don't really want to write in class A about every class, which must be loaded at start() method)

*addClassToLoad() takes an interface which has one method onLoad(), so it is getting called when method start() is called in class A

In code version:

class A:

public class A {
    private static ArrayList<ClassToLoad> classesToLoad;

    public static void addClassToLoad(ClassToLoad c) {
        if (classesToLoad == null)
            classesToLoad = new ArrayList<ClassToLoad>();
        classesToLoad.add(c);
    }

    public static void start() {
        for (ClassToLoad c : classesToLoad) {
            c.onLoad();
        }
    }
}

class B (and others (C, D etc.) like this one):

public class B {

    static {
        A.addClassToLoad(new ClassToLoad() {
            public void onLoad() {
                load();
            }
        });
    }

    private static void load() {
        // do something here on load ...
    }
}

class ClassToLoad:

public interface ClassToLoad {
    public void onLoad();
}
Milixyron
  • 105
  • 2
  • 9
  • It will help adding some outputs when calling key methods and then check if all is running as expected (Add `system.out.println()` calls in `A.addClassToLoad` and in `A.start` showing what's happening ). Maybe the A.start() method is called before any A.addClassToLoad(). – F.Igor May 31 '16 at 20:59
  • _(Tip)_ It is more elegant (instead of statics) to have _singletons_ instantiated once. I thinks **OSGi**'s modular system would cover some of the "class loading" functionality, as life cycle management of singletons and such. – Joop Eggen May 31 '16 at 21:00
  • @FIgor I already checked with `system.out.println()` and no response from static blocks, so exception is thrown when it looks at `classesToLoad` field, which stays null. – Milixyron Jun 01 '16 at 11:45
  • @JoopEggen Unfortunately, the way of singleton doesn't work either, `private static B instance = new B();` is not getting called. – Milixyron Jun 01 '16 at 11:57
  • if classesToLoad is staying null, some call to start() was made before any addClassToLoad() call. Check if there is some problem related with threads, syncronization. Also, you could be calling addClassToLoad in another different class . – F.Igor Jun 01 '16 at 15:12
  • @FIgor actually there's nothing to do with calling `start()` before/after `addClassToLoad()`, as the static blocks need to call `addClassToLoad()` first, then goes the `start()` (which is not getting called in static block), and unfortunately, static blocks aren't getting executed. Also nothing related with threads (I don't really know about syncronization that much). – Milixyron Jun 01 '16 at 16:27
  • Only when a class is used static initialisation will take place (though one can load with initialisation set to true). One could do things with ClassLoaders (URLClassLoader will do) but that needs care. Better would be to have a Container that handles all those things. OSGi in the form of felix or whatever would be one way. But I can understand, that would be quite removed from your original idea. **Singletons/Your case:** call a static method from outside the class once. – Joop Eggen Jun 01 '16 at 17:12
  • Consider the [*Service Provider* concept and `ServiceLoader`](https://docs.oracle.com/javase/tutorial/ext/basics/spi.html) – Holger Jun 30 '16 at 10:26

3 Answers3

0

This is the same question when you add a new JDBC driver, why you have to call Class.forName() to register a JDBC driver. Every JDBC driver class has a static initializer to register itself with DriverManager. Why? A class loader does not by default load all the classes in jar files. A class is loaded only when it is referenced during execution, which is smart as the class loader never has to load those unused classes into memory. So to resolve your issue, you have to manage to load those classes, like by Class.forName() before you call start(). If you use spring, you can create a list of all those classes in your configure. Spring also provides an util to scan packages for certain types of classes, then you can just specify a package name to scan.

S.F
  • 76
  • 4
0

Static fields will be set, and static initializers (static blocks) when ClassLoader will load class for the first time. Rembemer that this will happen when given class will be used for the first time as ClassLoader loads classes in lazy fashion (when needed)

Antoniossss
  • 31,590
  • 6
  • 57
  • 99
0

So it seems it's not possible for me to execute those static blocks, so I added every class, which I need to load, into class A, and that way they're actually loading without any problems (in project B). And in project A I need to add other classes, which I need to load, in the main class, obviously.

So I made those classes as Singletons, so they're actually loaded and are ready for "main" loading, launching and disposing. So the adding class looks like this:

A.addClassToLoad(B.getInstance());

I used class ClassToLoad as a generic class to load (sounds funny), though I renamed it to SystemCycle.

So the code as an example of class B now looks like this:

public class B implements SystemCycle {
    private static B instance = new B();

    private B() {}

    public static void getInstance() {
        return instance;
    }

    public void onLoad() { /* some code here */ }
    public void onLaunch() { /* some code here */ }
    public void onDispose() { /* some code here */ }

}

And SystemCycle class looks now like this:

public interface SystemCycle {

    public void onLoad();
    public void onLaunch();
    public void onDispose();

}

Well, that was obvious, because of example of class B.

And I even made small checking system, so if the user tries to call one of these methods, it will be ignored, as the class implementing SystemCycle checks whether the class A is actually loading, launching or disposing at that moment. But if not, it just can do return. (though if the usermade class doesn't check that, it can be abused by other usermade class).

P.S. addClassToLoad in my project is actually called addSystemToLoad, so I made it here this way to make an example easier to understand.

Small edit: I even tried something to do with annotations first, but even that thing didn't help me.

Milixyron
  • 105
  • 2
  • 9