3

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.

2
  • 2
    Looking at your variable names, I'm reminded of public static final int ONE = 2; Commented Nov 6, 2014 at 16:56
  • :P Whoops. Sorry about that. Commented Nov 6, 2014 at 17:04

3 Answers 3

2

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.

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

Comments

2

You can't use a in your static block when it hasn't been declared yet. So declare it before the static block:

public final class MyStaticClass {

    private MyStaticClass() {}

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

    static {
        a = new A();
        b = new B(a);
    }

}

(I assumed that calling your instance of A "b" and your instance of B "a" was a typo.)

2 Comments

Hi, yes this was a snippet, sorry the actual code is 10s of lines of initialisation. Why does moving the declaration effect it? I see what you're saying, but I though java compilation worked like C#, where the whole file is tokenized first, then compiled. I thought the init order was methods, fields, then initializers, so surely it shouldn't matter that the fields are declared physically further down the file?
I think it is to avoid circular dependencies, though I'm not sure in this case it is doing anything beneficial.
1

Static initialization is done in the order it's written in the code. So in your code it will enter the static block first and then will go to the variable declaration...

you cant use a variable before it is declared and thats why it wont compile...

public final class MyStaticClass {

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

    private MyStaticClass() {}

    static {

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


}



    }

4 Comments

I'm even more confused by this comment. So declaring the field, physically higher up in the class means it compiles? Why? I thought the entire file was tokenized before it's compiled. Why does A have to be a super class of B? B has a constructor which takes type A (internally B holds a reference to A).
you did messed up in writing the variables thats y i misunderstood... now check the edit
Cheers, this does work. I was just saying in another comment, do you know why this works? I would have thought the compiler tokenizes the entire file before compiling, so it should know what fields are declared regardless of order. Also another question mentions that fields are initialized before the initializer is called. Do you have any idea why changing the order effect the compiler results?
This is because Static initialization is done in the order it's written in the code.

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.