3

This is a bit of a tricky one to explain, but let's say I have two classes A and B. A contains a static list of factory objects that are registered by each object that provides such a factor. In this example B is such a class and provides an imaginary Factory implementation.

Class A:

public class A {
    protected static Map<String, Factory> registered = new HashMap<String, Factory>();
    protected static register(String name, Factory factory) {
        registered.put(name, factory);
    }

    public A() {
        // Do something with the factories we've registered
    }
}

Class B:

public class B {
    static {
        A.register("Foo", new Factory() {
            public Object create() {
                return new B();
            }
        });
    }

    public B() {
        // Create a new instance of class B
    }
}

In my program for some strange reason the static block within B is never called, so when I start interacting with A no factories have been registered so it can't do what it needs to do.

If I move the creation of each Factory into A directly there is no problem of course. I'm working under the assumption that because there are no clear references to B from any class that the compiler isn't recognising there's a link between A and B so doesn't bother with B at all. Is there anything I can do to work around this? I'd hoped to avoid adding each new factory into A directly as it makes maintenance more difficult than having new factories simply register themselves, but clearly having none of them work at all is even worse; still, I'd like to somehow get it to work as intended if I can.

In case it's relevant, the particular JVM I'm working with is the Android JVM, could this be a side-effect of some optimisation that that that JVM is using?

2
  • 10
    The static block will only get called if B class is loaded by JVM. If you have no reference to it anywhere, that won't happen. Commented Aug 30, 2013 at 17:24
  • 2
    Further, the static block is only run on the "first effective use" of the class, or something like that -- you have to create an instance or invoke a static method or some such. Commented Aug 30, 2013 at 17:56

1 Answer 1

7

You can read about class loading in this blog post. The point is that a class won't be loaded until it is referenced. And the static blocks of a class won't be executed until the class is loaded. The rules are

  1. an Instance of class is created using either new() keyword or using reflection using class.forName(), which may throw ClassNotFoundException in Java.
  2. an static method of Class is invoked.
  3. an static field of Class is assigned.
  4. an static field of class is used which is not a constant variable.
  5. if Class is a top level class and an assert statement lexically nested within class is executed.

A solution is to either instantiate B or call a no-op static method (or any of the above).

public class B {
    static {
        A.register("Foo", new Factory() {
            public Object create() {
                return new B();
            }
        });
    }

    public void static noOp() {}

    public B() {
        // Create a new instance of class B
    }
}

...

B.noOp();

The Oracle JVM spec states this here.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.