6

I've been coding in Java since 2005 and never thought that the code like that would actually be compilable:

public class FooClass {

    public static final String FOO = FooClass.FOO;

}

Intellij IDEA calls it "initialized with self assignment". If someone showed me this code and asked whether it will compile or not, I would be 100% sure it should be a compilation error, as the variable was not actually explicitly assigned to any value.

I am bit shocked, because even such a nonsense as below appears to be legit:

public class FooClass {
    public static final String FOO_1 = FooClass.FOO_2;
    public static final String FOO_2 = FooClass.FOO_1;
}

No brainer, that such a variable appear to contain null value effectively. But Gosh, why?

I bumped into this piece of code in my project, because it was source of a bug. Someone did a refactoring and replaced the actual string constant with this self reference. Convince me that this is not a Java language bug, because otherwise it is a nasty way to shoot oneself in the foot!

UPD: I tested it on Java 21 only.

8
  • Eclipse says "The assignment to variable FOO has no effect" (just a warning) Commented Apr 11 at 12:51
  • 2
    By using a qualified access (FooClass.FOO), you’re disabling the initialized check. Had you written FOO = FOO you would have received the compiler error. Commented Apr 11 at 17:18
  • 6
    The behavior is based on the following definition (JLS §16): “An access to its value consists of the simple name of the variable (or, for a field, the simple name of the field qualified by this)…” and FooClass.FOO is neither a simple name nor qualified with this. Hence, it’s not an access in the sense of the remainder of the section which restricts the matching accesses to be only allowed when the variable is definitely assigned. Commented Apr 11 at 17:28
  • 2
    Java goes to some length to forbid meaningless code. And your code is meaningless alright. But it’s hard to make a simple rule for how to distinguish your code from meaningful code. So it’s good that IDEs warn you, and the best we can do is take such warnings seriously. Commented Apr 11 at 19:48
  • 2
    A thought experiment: WHY are you so sure this should be a compilation error? It seems your reasoning is "anything that let's users shoot their feet is a language bug". But, not only is it almost impossible to reliably forbid "all the code that someone thinks should obviously be an error", if a language tried to do this, I think you would not enjoy programming in that language. There would be so many ad-hoc rules and restrictions that you would (rightly) say "no one can keep this in their head." And there would still be infinitely many things to complain that the language doesn't forbid. Commented Apr 23 at 15:51

1 Answer 1

1

The point is that static variables are first created all at once, and then initialized. At creation they are set to default value of their type. Initialization takes place in encountered textual order.

At least JLS 21 says:

4.12.5 Initial Values of Variables

Every variable in a program must have a value before its value is used:

• Each class variable, instance variable, or array component is initialized with a

default value when it is created (§15.9, §15.10.2):

[...]

– For all reference types (§4.3), the default value is null.

8.3.2 Field Initialization

If a declarator in a field declaration has a variable initializer, then the declarator has the semantics of an assignment (§15.26) to the declared variable.

If the declarator is for a class variable (that is, a static field) (§8.3.1.1), then the following rules apply to its initializer:

[...]

  • At run time, the initializer is evaluated and the assignment performed exactly once, when the class is initialized (§12.4.2).

12.3.2 Preparation of a Class or Interface

Preparation involves creating the static fields (class variables and constants) for a class or interface and initializing such fields to the default values (§4.12.5). This does not require the execution of any source code; explicit initializers for static fields are executed as part of initialization (§12.4), not preparation.

12.4.2 Detailed Initialization Procedure

  1. Next, execute either the class variable initializers and static initializers of the class, or the field initializers of the interface, in textual order, as though they were a single block.

You can also try:

public class Main { 
    static final int i = Main.i+1;
}

which is perfectly legal.

That one will show you that the described process takes place effectively:

public class Main {
    public static final String FOO_1 = Main.FOO_2+"Foo";
    public static final String FOO_2 = Main.FOO_1+"Bar";
}

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.