Search code examples
javastatic-initializationstatic-initializer

Static initializer cannot reference a field before it is defined


I have the following code with the error commented

public final class MyStaticClass {

    private MyStaticClass() {}

    static {

        a = new A();
        b = new B(a);    // Cannot access a field before it is defined
    }

    private static final A a;
    private static final B b;
}

I'm fairly new to using static initializers, but I have no idea why this will not compile. I've looked around a few of the posts on this topic, and have seen the order that initialisation runs, but this doesn't seem to violate the rule. By the time b is being initialized, a should already have been.

I have a work around, which would be to set up this class as a singleton, but doing so would make the code a little less readable. I'm keen to know what is going wrong here.


Solution

  • This is explained in JLS 8.3.3. In fact, there are a few ways to fix it.

    Use a qualified name of a:

    // #1
    public final class MyStaticClass {
        static {
            a = new A();
            b = new B(MyStaticClass.a);
        }
    
        private static final A a;
        private static final B b;
    }
    

    If a and b were instance fields being initialized in an instance initializer, a could be qualified as this.a.

    Put the forward reference to a on the left-hand of an assignment:

    // #2
    public final class MyStaticClass {
        static {
            b = new B(a = new A());
        }
    
        private static final A a;
        private static final B b;
    }
    

    And of course put the declaration textually before the reference:

    // #3
    public final class MyStaticClass {
        private static final A a;
        private static final B b;
    
        static {
            a = new A();
            b = new B(a);
        }
    }
    

    According to the JLS, #3 is not technically necessary ("these class variables are in scope"), rather this is designed to catch a particular kind of error where fields are initialized out of order:

    public final class MyStaticClass {
        private static final B b = new B(a); // a is null
        private static final A a = new A();
    }
    

    (Though I just showed you two ways to thwart it and make the error anyway.)

    I'd recommend #1 or #3 since #2 is a bit esoteric. You don't seem to be making the error this rule is designed to catch.