Forums Register Login

Why Do enum Contructors Run When They Do?

+Pie Number of slices to send: Send
I am trying to comprehend enum constructors. I am not having much success. Here's my code:



Here's my output:

Enum constructor call 1 for E.
Enum constructor call 2 for F.
Enum constructor call 3 for G.
Enum static.
Class static.
Class constructor.


From this I glean that all instances of an enum are created at the same time, with a unique call to the enum's constructor for each. That surprises me, as I would have expected construction to have been lazier. Am I right that accessing any one of an enum's values results in all of them being constructed? If so, is there a reason for this (other than, "that's the way things are")?

Also, it appears that, while a class's static method runs before its constructor is called for the first time, an enum's static method runs after its constructor is called for the last time. Again, this surprises me and I'm curious to know why the static/constructor order is not the same for enums as it is for classes.

Can anyone enlighten me?

Also, while the code above compiles and runs just fine, replacing Line 31 with this:
produces an, "illegal reference to static field from initializer" compile-time error. Why is that an error, given that the static method successfully accesses the static field, and the static method can be called from the initializer? (And, is that error message giving me a hint that enums have initializers, but they don't have constructors?)
1
+Pie Number of slices to send: Send
There's actually nothing special about this behavior, regular classes work the same way. Every enum constant is actually a static field that is initialized like this:


Static field initializers are run as soon as you reference the class, meaning you would trigger all the constructor calls even when all you did was call one of the static methods declared by Enum.
1
+Pie Number of slices to send: Send
The values of an enum are initialized when the class is loaded (like static fields).

The enum values are initialised prior any other field in the class (classes are always initialized in textual order), thus accessing static fields within the constructor is not (directly) allowed to prevent errors resulting from this.
You should take a look at the value of constructorCalls after the enum class got loaded / try to start with a value other than zero.

You can compare your enum with the following class version which behaves exactly the same way:
+Pie Number of slices to send: Send
Thanks, fellows. That makes perfect sense.

That business about indirect access to the static fields is a bit eye-popping. If I initialize it, that initialization takes place after it has already been accessed indirectly from the constructor through the count method. Is it safe to assume that it contains zero when the class loads, or is it undefined at that time? If the former, I don't see why access from the constructor should be illegal. Inadvisable, maybe, but not illegal.
1
+Pie Number of slices to send: Send
Fields are always initalized with the default value of their type (0 for primitive types, null for references) (even when explicitly initialized to their default value afterwards, reason why I personally ommit explicit initializations to default values).

Reason for this rule:

http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.9 wrote:It is a compile-time error to reference a static field of an enum type from constructors, instance initializers, or instance variable initializer expressions of the enum type, unless the field is a constant variable (§4.12.4).
[...]
Without the rule on static field access, apparently reasonable code would fail at run time due to the initialization circularity inherent in enum types. (A circularity exists in any class with a "self-typed" static field.) Here is an example of the sort of code that would fail:

Static initialization of this enum would throw a NullPointerException because the static variable colorMap is uninitialized when the constructors for the enum constants run. The restriction above ensures that such code cannot be compiled. However, the code can easily be refactored to work properly:

The refactored version is clearly correct, as static initialization occurs top to bottom.

 
+Pie Number of slices to send: Send
 

Tobias Bachert wrote:Without the rule on static field access, apparently reasonable code would fail at run time due to the initialization circularity inherent in enum types. (A circularity exists in any class with a "self-typed" static field.) Here is an example of the sort of code that would fail:


Interesting. But for the JLS prohibition, that code would be, at least syntactically, valid Java. The null-pointer exception would occur, but so it does in this version, which compiles successfully, yet fails for the same reason:

Short of solving The Halting Problem, I can see why the compiler can't catch this, and I'm glad it does catch it in the direct-access case. I'm still a bit surprised it was elevated from being a coding mistake to an actual violation of the JLS, though. Although I wouldn't want to defend it as production code, the "indirect" method I used in my earlier example does make valid use of access by the constructor to a static field, albeit in a way that must accept the fact that the field has not been initialized (or, more precisely, that it is set to the default initial value of zero when the constructor does get access to it). Direct access to that field, had it been allowed, would have worked just as well.
1
+Pie Number of slices to send: Send
Well, this has been an interesting day. Let me see if I can summarize what I think I've learned:

An enum is a kind of class where, instead of constructors being invoked by the new keyword in client code, a fixed set of instances of the class are created when the class is loaded.

A reference to each of those instances is kept in a static class variable that is initialized when the class is loaded by calls to the constructor with the signature matching the parameter list (if any) that follows the static variable's name.

Client code can access a specific instance by a static reference to its named variable, but it can also have its own variable of the type of the class (the enum, that is) and store a reference to one of the instances in that variable. Calls to class methods and references to instance fields can be made just as with any other class variable.

So it would seems that classes and enums are almost identical, the difference being that you decide at compile time how many instances of the enum there will be at run time, that they are created all at once, and you can refer to them by static names as well as by class variables, while a class can be written to have zero to any number of instances created by client code at run time, with no predefined static variables holding references to any of those instances.

That is kind of mind-blowing, but it really drives home why Joshua Bloch advises the single-instance enum as the best way to implement a singleton. That's a long way from my first days of thinking enums were just opaque constants to prettify my code.
+Pie Number of slices to send: Send
See if you can find the old Java5 version of the Java™ Tutorials about enums. Read it very carefully and compare it with the current version.
+Pie Number of slices to send: Send
 

Campbell Ritchie wrote:See if you can find the old Java5 version of the Java™ Tutorials about enums.


I wasn't able to find that, but my copy of "Head First Java" addresses Java 5. Its (three!) pages on enums mentions something the current tutorial does not: constant-specific class bodies. Apparently, these are somewhat like anonymous inner classes, but applied individually to chosen members of the fixed set of instances created when an enum class is loaded. Perhaps a partial solution to the problems that might arise from the fact of an enum not being able to extend another class (which is another difference between an enum and a class that I hadn't been aware of).

Is that what you had in mind?
+Pie Number of slices to send: Send
Constant-specific class bodies:

+Pie Number of slices to send: Send
If I remember correctly, the current Java™ Tutorials says Java enums are more powerful than those in other languages. The first version with enums in had a little extra bit after other languages: which are simply glorified integers.

They must have had complaints an had to take that bit out.
+Pie Number of slices to send: Send
 

Stevens Miller wrote:Perhaps a partial solution to the problems that might arise from the fact of an enum not being able to extend another class (which is another difference between an enum and a class that I hadn't been aware of).


They can, however, implement interfaces, which is extremely useful; and the code is just like Paul's example above, despite the fact that each instance is (essentially) a static constant.

I haven't yet tried to write one that implements a v8 @functional interface yet - nor, for that matter, worked out why I might want to ... but its an interesting idea.

Winston
1
+Pie Number of slices to send: Send
In my opinion enums that implement function interfaces are somewhat useful as filter etc. as they can improve readability a bit, e.g:

(One disadvantage of enums: they cannot be generic)
+Pie Number of slices to send: Send
 

Tobias Bachert wrote:(One disadvantage of enums: they cannot be generic)


Not in as much that they can't define a type; but each method you implement can.

It does make the "boilerplate" kinda clumsy though - rather like it is for v8 APIs in general.

Winston
I didn't say it. I'm just telling you what this tiny ad said.
a bit of art, as a gift, the permaculture playing cards
https://gardener-gift.com


reply
reply
This thread has been viewed 888 times.
Similar Threads
Question on for loop
order when constructing
Enum Constructor doesn't access non-final static variable
Generics Enum type values?
Doubt regarding Innerclasses
More...

All times above are in ranch (not your local) time.
The current ranch time is
Mar 28, 2024 11:15:18.