I am new to enums in java and I am confused as to why this code compiles fine
enum Scale5 {
GOOD(), BETTER(), BEST();
static Scale5 s=GOOD;
}
But this code fails:
enum Scale5 {
GOOD(), BETTER(), BEST();
Scale5 s=GOOD;
}
And I get the error : illegal reference to static field from initializer.
I dont understand the reason.I am relatively inexperienced in enums so please dump it down for me.Thanks a lot!
The question asked here Cannot refer to the static enum field within an initializer? is the exact opposite of what I have asked.In my case declaring s as static compiles the code just fine.
From Java Language Specification:
It is a compile-time error to reference a static field of an enum type
that is not a constant variable (§4.12.4) from constructors, instance
initializer blocks, or instance variable initializer expressions of
that type.
Think of an enum like this:
public final class Scale5
{
public static final Scale5 GOOD = new Scale5();
public static final Scale5 BETTER = new Scale5();
public static final Scale5 BEST = new Scale5();
static Scale5 s = GOOD;//works because GOOD is initialized first;
Scale5 ss = GOOD;//doesn't work because in order to initialize GOOD,
//ss must be assigned an object that is not yet initialized;
}
It is very simple. The first case works because the compiler defines all the enum constants, then initializes static variables. Hence, GOOD at that point already exists and all is fine.
On the second case, the variable exists for all the enum constants, hence, when you are creating GOOD (or BETTER or BEST), GOOD must already be defined and bound. This is of course, illegal.
Consider following example:
class X {
public static final X GOOD = new X();
X y = GOOD;
static X z = GOOD;
public static void main(String[] args) {
System.out.println(GOOD);
System.out.println(GOOD.y);
System.out.println(X.z);
}
}
Output:
X#1db9742
null
X#1db9742
As you see value of GOOD.y is null. That is because at the moment of initializing value of
public static final X GOOD = new X();
when you invoked new X() you ware using current value of GOOD which is null until new X() will be finished. While in case of normal classes this behaviour is allowed, it was forbidden for enums probably because it was too often misunderstood.
In case of static fields they are executed problem dissipaters because they are executed in order of declaration, so
static X z = GOOD;
will be executed when we are sure that GOOD was initialized with proper value.
Related
I was reading the question here : Java : in what order are static final fields initialized?
According to the answer
"except that final class variables and fields of interfaces whose
values are compile-time constants are initialized first ..."
I think this is not correct because the following will fail :
static {
String y = x;
}
public static final String x = "test";
In the static block, x is not recognized. Can anyone please comment if that answer is correct ?
The order of initialization doesn't change the fact that the JLS doesn't let you refer to variables before they're declared in various cases. This is described in JLS§8.3.3:
Use of class variables whose declarations appear textually after the use is sometimes restricted, even though these class variables are in scope (§6.3). Specifically, it is a compile-time error if all of the following are true:
The declaration of a class variable in a class or interface C appears textually after a use of the class variable;
The use is a simple name in either a class variable initializer of C or a static initializer of C;
The use is not on the left hand side of an assignment;
C is the innermost class or interface enclosing the use.
That's why your code gets this compilation erorr:
error: illegal forward reference
The statement that static fields that are constant variables are initialized first is indeed defined in JLS§12.4.2:
Otherwise, record the fact that initialization of the Class object for C is in progress by the current thread, and release LC.
Then, initialize the static fields of C which are constant variables (§4.12.4, §8.3.2, §9.3.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.
As you can see, constant variables are initialized in step 6, but others in step 9.
This demonstrates the behavior:
public class Example {
static String y;
static {
y = foo();
}
static String foo() {
return x.toUpperCase();
}
public static final String x = "test";
public static void main(String[] args) throws Exception {
System.out.println(x);
System.out.println(y);
}
}
That compiles, and outputs:
test
TEST
In contast, if you change the x line so it's not constant anymore:
public static final String x = Math.random() < 0.5 ? "test" : "ing";
It compiles, but then fails because x is null as of y = foo();.
For the avoidance of doubt: I don't recommend using methods to initialize fields like that. :-)
As this answer states:
... they are initialized in the order in which they appear in the source.
You are absolutely right, your example fails, because you are trying to use a variable that is declared after the usage. Since static block are executed in order of the source code, you are absolutely correct and should suggest and edit for that answer, because this statement is invalid:
except that final class variables ... are initialized first.
This is the sample code I have:
enum A {
A,
}
class TestA {
A a;
public static void main(String[] args) {
final TestA testA = new TestA();
System.out.println(testA.a);
System.out.println(testA.a.A);
}
}
Which will print:
null
A
If the default value for an uninitialized instance Enum variable is null, how does accessing an instance of an Enum work?
A.A is a static variable. It's a bad idea, but authorized, to access static variable of a class using a variable referring to an instance of that class, even if it's null. That's not limited to enums:
Integer i = null;
System.out.println(i.MAX_VALUE);
runs fine. But it should really be written as
System.out.println(Integer.MAX_VALUE);
Enum constants are essentially static members, so they obey the exact same rules as static members.
The reason it works is exactly the reason why ((System) null).out will not cause an NPE, because its turned into a static member access which does not use the null in any way.
This question already has answers here:
Final fields initialization order
(2 answers)
Closed 6 years ago.
In our team we found some strange behaviour where we used both static and final qualifiers. This is our test class:
public class Test {
public static final Test me = new Test();
public static final Integer I = 4;
public static final String S = "abc";
public Test() {
System.out.println(I);
System.out.println(S);
}
public static Test getInstance() { return me; }
public static void main(String[] args) {
Test.getInstance();
}
}
When we run the main method, we get a result of:
null
abc
I would understand if it wrote null values both times, since the code of static class members is executed from top to bottom.
Can anyone explain why this behaviour is happening?
These are the steps taken when you run your program:
Before main can be run, the Test class must be initialized by running static initializers in order of appearance.
To initialize the me field, start executing new Test().
Print the value of I. Since the field type is Integer, what seems like a compile-time constant 4 becomes a computed value (Integer.valueOf(4)). The initializer of this field has not yet run, printing the initial value null.
Print the value of S. Since it is initialized with a compile-time constant, this value is baked into the referencing site, printing abc.
new Test() completes, now the initializer for I executes.
Lesson: if you rely on eagerly initialized static singletons, place the singleton declaration as the last static field declaration, or resort to a static initializer block that occurs after all other static declarations. That will make the class appear fully initialized to the singleton's construction code.
S is a compile-time constant, following the rules of JLS 15.28. So any occurrence of S in the code is replaced with the value which is known at compile-time.
If you change the type of I to int, you'll see the same for that, too.
You have strange behavior due to the Integer data type. Regarding JLS 12.4.2 static fields are initialized in the order you write it, BUT compile-time constants are initialized first.
If you do not use the wrapper type Integer but the int type, you get the behavior you want.
Your Test compiles into:
public class Test {
public static final Test me;
public static final Integer I;
public static final String S = "abc";
static {
me = new Test();
I = Integer.valueOf(4);
}
public Test() {
System.out.println(I);
System.out.println("abc");
}
public static Test getInstance() { return me; }
public static void main(String[] args) {
Test.getInstance();
}
}
As you can see, the constructor for Test gets called before I is initialized. This is why it prints "null" for I. If you were to swap the declaration order for me and I, you would get the expected result because I would be initialized before the constructor is invoked. You can also change the type for I from Integer to int.
Because 4 needs to get autoboxed (i.e., wrapped in an Integer object), it is not a compile-time constant and is part of the static initializer block. However, if the type were int, the number 4 would be a compile-time constant, so it would not need to be explicitly initialized. Because "abc" is a compile-time constant, the value of S is printed as expected.
If you would replace,
public static final String S = "abc";
with,
public static final String S = new String("abc");
Then you would notice the output of S is "null" as well. Why does that happen? For the same reason why I also outputs "null". Fields like these that have literal, constant values (that do not need autoboxing, like String) are attributed with the "ConstantValue" attribute when compiled, which means that their value can be resolved simply by looking into the class' constant pool, without needing to run any code.
To distinguish between an instance field and a local variable of the same name we can qualify access to the field with the prefix this.:
class Test {
public final Foo x;
public Test(Foo x) {
this.x = x;
}
}
I'm trying to do the same thing in a static context by qualifying access with the class name:
import java.util.*;
class Test {
public static final Map<String,Object> map;
static {
Map<String,Object> map = new HashMap<>();
// ...
// assume I fill the map with useful data here
// ...
// now I want to freeze it and assign it to the field
Test.map = Collections.unmodifiableMap(map);
}
}
The compiler wants nothing to do with this code. I have several variables like this and for all of them it keeps yelling: "cannot assign a value to final variable". If I don't assign to it, it complains "variable not initialized" instead. If I assign to the static field at the beginning and try to make the map unmodifiable afterwards, it complains "variable might already have been assigned". It's not happy with anything.
Is this a flaw in the language, or a bug in the compiler? What's the best way to squash the compiler into doing as its told?
The easiest way to solve is as follows:
import java.util.*;
class Test {
public static final Map<String,Object> map;
static {
Map<String,Object> contents = new HashMap<>();
map = Collections.unmodifiableMap(contents);
}
}
Somehow it seems that if you qualify the constant with the class name in Java 8, the compiler won't have it.
Update
After some more digging, it seems that the Java Language Specification explicitly states that the simple (unqualified) name needs to be used for the assignment of final fields (highlighting mine):
For every access of a local variable or blank final field x, x must be
definitely assigned before the access, or a compile-time error occurs.
Similarly, every blank final variable must be assigned at most once;
it must be definitely unassigned when an assignment to it occurs.
Such an assignment is defined to occur if and only if either the
simple name of the variable (or, for a field, its simple name
qualified by this) occurs on the left hand side of an assignment
operator.
It looks like it works to say
public static final <something> x;
static {
x = <whatever>;
}
but not
public static final <something> x;
static {
MyClass.x = <whatever>;
}
I'm not sure why, but that's the behavior I'm getting. To avoid this in your own example, simply change Test.map to map, and change the name of the other map variable.
P.S. Robby's answer explains the reason for the behavior.
I realized a workaround, using a method to init the field instead of a static block. Inside the method the variable can of course be named whatever the foo it wants to be named:
public static final Map<String,Object> map = initMap();
private static Map<String,Object> initMap() {
...
}
in java we are able to "invoke a static method with class name" and also "invoke a static method with an object"
what is the difference between "invoking a static method with class name" and "invoking a static method with an object" in java?
There is no difference but the recommendation is to call the static methods in a staic way i.e. using the ClassName. Generally static analayzers will report an error if you don't do so.
An important thing to understand here is that static method are stateless and hence calling them with an instance is confusing for someone reading your code. Because no matter what instance you use to call the static method, result will remain the same. This is because a static method belongs to a class but not to an object.
Internally, new ClassName().staticMethod(); is considered as ClassName.staticMethod(). So there is no difference. It just causes confusion.
Consider an example
public class StaticDemo {
static int staticVar = 10;
public static void main(String [] args){
System.out.println(new StaticDemo().staticVar);
System.out.println(StaticDemo.staticVar);
}
}
This outputs
10
10
`
even if you write new StaticDemo().staticVar, it will be considered as StaticDemo.staticVar. So there is no difference at all and always use the Class notation to avoid confusion.
There is no difference at all. But since static methods are at the class level, you can access them using the class name. Though invoking them using class object is possible, it creates a confusion. Why would you like to invoke them for every object if its value is going to be the same?
No, there is none. Apart from possible confusion... A reader of such code cannot reliably tell static from non static members/methods apart if you use an instance to access them:
public class X
{
public static final String foo = "foo";
public static String bar() { return "bar"; }
public static void main(final String... args)
{
final X x = new X();
System.out.println(X.foo); // "foo"
System.out.println(x.foo); // "foo" too -- same reference
System.out.println(X.bar()); // "bar"
System.out.println(x.bar()); // "bar" too -- same reference
}
}
As this is confusing, it is generally preferred to use the class to refer to such members/methods.
Static methods do NOT operate on a class. They are just bound to class instead of to member of that class. This means that they don't have access to any non static members of the class. Other than that, they are not very different than the non-static methods.
In SCJP guide book. having a example as following:
class Frog {
static int frogCount = 0; // Declare and initialize
// static variable
public Frog() {
frogCount += 1; // Modify the value in the constructor
}
}
class TestFrog {
public static void main (String [] args) {
new Frog();
new Frog();
new Frog();
System.out.print("frogCount:"+Frog.frogCount); //Access
// static variable
}
}
But just to make it really confusing, the Java language also allows
you to use an object reference variable to access a static member
Frog f = new Frog();
int frogs = f.frogCount; // Access static variable
// FrogCount using f
In the preceding code, we instantiate a Frog, assign the new Frog
object to the reference variable f, and then use the f reference to
invoke a staticmethod! But even though we are using a specific Frog
instance to access the staticmethod, the rules haven't changed. This
is merely a syntax trick to let you use an object reference variable
(but not the object it refers to) to get to a staticmethod or
variable, but the staticmember is still unaware of the particular
instance used to invoke the staticmember. In the Frog example, the
compiler knows that the reference variable f is of type Frog, and so
the Frog class staticmethod is run with no awareness or concern for
the Frog instance at the other end of the f reference. In other
words, the compiler cares only that reference variable f is declared
as type Frog.
I hope this will help you