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 example below:
public void sayGoodbyeInEnglish() {
class EnglishGoodbye {
public static final String farewell = "Bye bye";
public void sayGoodbye() {
System.out.println(farewell);
}
}
System.out.println(EnglishGoodbye.farewell);
EnglishGoodbye myEnglishGoodbye = new EnglishGoodbye();
myEnglishGoodbye.sayGoodbye();
}
In class EnglishGoodbye the variable farewell is allowed? Why? I'm confused. Why is that allowed but no static variable? I understand why it cannot access the members of the enclosing scope unless they are a compiler time constant as those variables cease to exist when the function ends but the class may not. Right? Im just confused on this.
Thanks!
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.
来源:https://stackoverflow.com/questions/27095847/why-static-final-variables-are-accepted-in-local-classes