I\'ve googled this extensively to no avail. I cannot seem to wrap my head around this concept. Why are static final fields accepted in local classes? Such as the following examp
It's not, in general.
But farewell
is a special kind of static final: one whose value is a constant, as defined by JLS 15.28. That means that it's not being initialized at that location, which is what's actually disallowed in non-static classes (including local classes), as per JLS 8.1.3.
The JLS states this explicitly (and in bold font) in 8.1.3 (note the "unless" part):
It is a compile-time error if an inner class declares a member that is explicitly or implicitly static, unless the member is a constant variable (§4.12.4).
If you change that line to either remove the final
modifier or make the expression non-constant (for example, new String("Bye bye")
), then you'll get the compilation error you expect:
Test.java:5: error: Illegal static declaration in inner class EnglishGoodbye
public static final String farewell = new String("Bye bye");
^
modifier 'static' is only allowed in constant variable declarations
1 error
A bit more:
The reason this is allowed is that constant variables are treated specially by the compiler. In particular, it's allowed to inline them -- the resulting bytecode doesn't have the farewell
field at all! If you decompile the class (javap -c YourClassName
), you'll see something like this:
public void sayGoodbyeInEnglish();
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String Bye bye
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
...
The above corresponds to this line:
System.out.println(EnglishGoodbye.farewell);
It's a bit daunting, but notice that "3:" line. The program isn't loading the value of the field farewell
, it's loading constant #3 (which it note in a comment is the String "Bye bye") (you can see a list of the bytecodes on wikipedia).
Because farewell
is a constant variable (and not a "real" static member), and thus can be inlined in the code, it doesn't matter where you define it -- the variable's lifecycle is essentially that of the whole JVM, not any one class or instance, and thus it can be declared anywhere.