evum constructor gotcha

R

Roedy Green

There seems to be a problem accessing the static final values in the
enum constructor. Is this a bug, or one of those quirky features of
the way enums are implemented?
--
Roedy Green Canadian Mind Products
http://mindprod.com

"Patriotism is fierce as a fever, pitiless as the grave, blind as a stone, and as irrational as a headless hen."
~ Ambrose Bierce (born: 1842-06-24 died: 1914 at age: 71)
 
E

Eric Sosman

Roedy said:
There seems to be a problem accessing the static final values in the
enum constructor. Is this a bug, or one of those quirky features of
the way enums are implemented?

Can you post an example of the problem?
 
L

Lew

Roedy said:
There seems to be a problem accessing the static final values in the
enum constructor. Is this a bug, or one of those quirky features of
the way enums are implemented?

The JLS (§8.9) says:
It is a compile-time error to reference a static field of an enum type
that is not a compile-time constant (§15.28) from constructors, instance
initializer blocks, or instance variable initializer expressions of that
type. It is a compile-time error for the constructors, instance initializer
blocks, or instance variable initializer expressions of an enum constant e
to refer to itself or to an enum constant of the same type that is declared
to the right of e.

If your static final value is not a compile-time constant (§15.28), then it is
your bug due to one of those quirky features of enums.

The reason is that the enum constants are defined prior to other static
fields. JLS:
 
R

Roedy Green

There seems to be a problem accessing the static final values in the
enum constructor. Is this a bug, or one of those quirky features of
the way enums are implemented?

It seems to work like this:

Enum constructors cannot access the enum static fields. However, they
can invoke the enum’s static methods that access the static fields,
but they won’t get the right values. The problem is static
initialisation has not been done at the time the enum constructor
constant constructers are invoked, so using static methods will just
see zeros/nulls. Why? I don’t know. You’d think statics should be
initialised first, the way they are with any other classes.
Constructors may, however, directly access static final in-lineable
constants known at compile time.

Question: why not do the static inits first? This leads to weird
coding without static constants.
--
Roedy Green Canadian Mind Products
http://mindprod.com

"Patriotism is fierce as a fever, pitiless as the grave, blind as a stone, and as irrational as a headless hen."
~ Ambrose Bierce (born: 1842-06-24 died: 1914 at age: 71)
 
A

Arne Vajhøj

Roedy said:
It seems to work like this:

Enum constructors cannot access the enum static fields. However, they
can invoke the enum’s static methods that access the static fields,
but they won’t get the right values. The problem is static
initialisation has not been done at the time the enum constructor
constant constructers are invoked, so using static methods will just
see zeros/nulls. Why? I don’t know. You’d think statics should be
initialised first, the way they are with any other classes.
Constructors may, however, directly access static final in-lineable
constants known at compile time.

Question: why not do the static inits first? This leads to weird
coding without static constants.

http://mindprod.com/jgloss/sscce.html

Arne
 
E

Eric Sosman

Roedy said:
It seems to work like this:

Enum constructors cannot access the enum static fields. However, they
can invoke the enum’s static methods that access the static fields,
but they won’t get the right values. The problem is static
initialisation has not been done at the time the enum constructor
constant constructers are invoked, so using static methods will just
see zeros/nulls. Why? I don’t know. You’d think statics should be
initialised first, the way they are with any other classes.
Constructors may, however, directly access static final in-lineable
constants known at compile time.

Question: why not do the static inits first? This leads to weird
coding without static constants.

Perhaps I'm missing something, but this seems simple: To
initialize the static field, you need to construct an enum
instance for it to refer to. How can the constructor get the
value of a reference to an instance that has not yet finished
being constructed?

But, as I said, I may have misunderstood ... Again, maybe
some example code would steer me straight.
 
L

Lew

Roedy said:
It seems to work like this:

Enum constructors cannot access the enum static fields. However, they
can invoke the enum’s static methods that access the static fields,
but they won’t get the right values. The problem is static
initialisation has not been done at the time the enum constructor
constant constructers are invoked, so using static methods will just
see zeros/nulls. Why? I don’t know. You’d think statics should be
initialised first, the way they are with any other classes.
Constructors may, however, directly access static final in-lineable
constants known at compile time.

Question: why not do the static inits first? This leads to weird
coding without static constants.

Static initialization *is* done first with enums. Each enum constant is a
static field, defined prior to other static fields.
 
L

Lew

Lew said:
Static initialization *is* done first with enums. Each enum constant is
a static field, defined prior to other static fields.

And if you were to read the section of the JLS to which Patricia Shanahan
alluded, you see the detailed rationale, under the "Discussion" beginning:
 
R

Roedy Green

There seems to be a problem accessing the static final values in the
enum constructor. Is this a bug, or one of those quirky features of
the way enums are implemented?

A strange thing I discovered is you can fix the problem my converting
your constants from static to instance. Then they do get initialised
on time.

This is so Mickey Mouse.
--
Roedy Green Canadian Mind Products
http://mindprod.com

"Patriotism is fierce as a fever, pitiless as the grave, blind as a stone, and as irrational as a headless hen."
~ Ambrose Bierce (born: 1842-06-24 died: 1914 at age: 71)
 
J

Joshua Cranmer

Roedy said:
There seems to be a problem accessing the static final values in the
enum constructor. Is this a bug, or one of those quirky features of
the way enums are implemented?

The initialization of enums starts with the enum constants in ascending
order (by ordinal, i.e. order of specification), and then the other
static initializers proceed in the standard order.

In particular, this implies:
* static fields that are not constants cannot be accessed by enum
constructors
* enum constructors can only access the enum values that precede it.
 
L

Lew

Roedy said:
A strange thing I discovered is you can fix the problem my converting
your constants from static to instance. Then they do get initialised
on time.

This is so Mickey Mouse.

Static constants in enums are initialized prior to the enum constants. Other
static final variables are not, as stated in the JLS.

Instance variables are naturally initialized during instance initialization.
That makes perfect sense.
 
L

Lew

Joshua said:
The initialization of enums starts with the enum constants in ascending
order (by ordinal, i.e. order of specification), and then the other
static initializers proceed in the standard order.

In particular, this implies:
* static fields that are not constants cannot be accessed by enum
constructors
* enum constructors can only access the enum values that precede it.

As Patricia Shanahan cited:
 
M

Mike Schilling

Patricia said:
Incidentally, the JLS suggests a simple solution to the limitations
of
enum constructors - do the work in a static initializer block that
iterates over the values() elements. The static initializer block
can
be placed after any static declarations on which it depends,
ensuring
that their initializers have already run when it executes.

It's a bit unfortunate that the implementation of enums is showing
through, but c'est la vie.
 
L

Lew

Mike said:
It's a bit unfortunate that the implementation of enums is showing
through, but c'est la vie.

I comfort myself with the thought that one can still develop pre-Java 5-style
type-safe enums if the built-in variety don't suit.

Like the new for-each loop, built-in enums serve a niche purpose. They are
not nor were ever meant to be a one-size-fits-all general solution. They have
an incredible range of power - arbitrary constructors, overridable custom
methods, tunable toString() and fromString() methods, freely-definable
attributes and more - that come from them actually being classes, but there
are inevitably restrictions that arise from their special characteristics,
notably the convenient syntactic and semantic features of enum constants. If
you reach the tipover point where the restrictions overbalance the advantages,
use some other kind of class than enum.

Otherwise, indeed, c'est la vie.
 
J

Joshua Cranmer

Mike said:
It's a bit unfortunate that the implementation of enums is showing
through, but c'est la vie.

Even if implemented differently, the two conditions must probably remain
the same. Consider that the usage of enums would most likely require
them to act as static variables. Now consider how to initialize them.

Since each instance is a variable of the type, it has to be initialized
before being used--there must therefore be an order on initialization.
In principle, you could ask the compiler to be nice and try to find an
order that permits the used references, but this must necessarily fail
if the relations fail to form a directed acyclic graph. Java's authors
do not like arbitrary initialization orders (even C++ requires class
objects' members to be initialized in a certain order), so the potential
reordering would either have to be completely specified or banned
altogether. To me, aesthetic reasons would dictate that the latter
course of action be followed.

The other question to consider is whether or not to initialize the
variables before or after class initialization--I'm not going to
consider interspersing for much the same aesthetic reasons that led me
to the conclusion in the preceding paragraph. Which would be more useful
to the average programmer, do you think: to be able to use the enum
variables within static initializers, or to use static initializers in
enum variables? Through use of nested classes, it is possible to achieve
the other effect from what is chosen, so the question is more one of the
likelier usage. Another thing to keep in mind is that the instances are
declared at the top of the class; since initialization follows the
left-to-right order, it stands to reason that the at-least pseudo-static
enum variables should be initialized first.

Now, one option that could sidestep mandatory initialization order is to
use double-indirection on enum variables and do a lot of lazy binding.
To do the lazy binding globally would likely be a performance hit and
could constrain usage of enums in unexpected ways. To limit the effects
to the initialization of enums would reduce the scope of the problem,
but it introduces the side effect of non-portability of enums. Not to
mention that there is considerable complexity to the use of lazy binding
and double-indirection in Java for a rather limited and suspect benefit.
 
M

Mike Schilling

Lew said:
I comfort myself with the thought that one can still develop
pre-Java
5-style type-safe enums if the built-in variety don't suit.

Sure. We use a variant that allows new members to be added at
run-time (for cross-version distributed compatibility.) I'd love it
if the built-in ones supported that, but, hell, so I generate 50 lines
of code instead of 10.
 
L

Lew

Joshua said:
Even if implemented differently, the two conditions must probably remain
the same. Consider that the usage of enums would most likely require
them to act as static variables. Now consider how to initialize them.

Since each instance is a variable of the type, it has to be initialized
before being used--there must therefore be an order on initialization.
In principle, you could ask the compiler to be nice and try to find an
order that permits the used references, but this must necessarily fail
if the relations fail to form a directed acyclic graph. Java's authors
do not like arbitrary initialization orders (even C++ requires class
objects' members to be initialized in a certain order), so the potential
reordering would either have to be completely specified or banned
altogether. To me, aesthetic reasons would dictate that the latter
course of action be followed.

The other question to consider is whether or not to initialize the
variables before or after class initialization--I'm not going to
consider interspersing for much the same aesthetic reasons that led me
to the conclusion in the preceding paragraph. Which would be more useful
to the average programmer, do you think: to be able to use the enum
variables within static initializers, or to use static initializers in
enum variables? Through use of nested classes, it is possible to achieve
the other effect from what is chosen, so the question is more one of the
likelier usage. Another thing to keep in mind is that the instances are
declared at the top of the class; since initialization follows the
left-to-right order, it stands to reason that the at-least pseudo-static
enum variables should be initialized first.

Now, one option that could sidestep mandatory initialization order is to
use double-indirection on enum variables and do a lot of lazy binding.
To do the lazy binding globally would likely be a performance hit and
could constrain usage of enums in unexpected ways. To limit the effects
to the initialization of enums would reduce the scope of the problem,
but it introduces the side effect of non-portability of enums. Not to
mention that there is considerable complexity to the use of lazy binding
and double-indirection in Java for a rather limited and suspect benefit.

That seems the very antithesis of "Mickey Mouse" to me.
 
R

Roedy Green

Static constants in enums are initialized prior to the enum constants. Other
static final variables are not, as stated in the JLS.

Instance variables are naturally initialized during instance initialization.
That makes perfect sense.

But it doesn't make sense that instance constants are evaluated prior
to static constants.

I know it is in the JLS. That is still Mickey Mouse language design.


--
Roedy Green Canadian Mind Products
http://mindprod.com

"Patriotism is fierce as a fever, pitiless as the grave, blind as a stone, and as irrational as a headless hen."
~ Ambrose Bierce (born: 1842-06-24 died: 1914 at age: 71)
 
A

Andreas Leitgeb

Joshua Cranmer said:
Even if implemented differently, the two conditions must probably remain
the same.

From the "in a different world"-departent:

It would be possible to have the compiler reserve space for all the
enum-consts, and only then run each's constructor. This would still
not yet enable cyclic *use*, but cyclic *referencing*:

// imaginary sample:
enum ThingsAndAntis {
FOO(BAR), BAR(FOO), X(Y), Y(X);
ThingsAndAntis(ThingsAndAntis anti) {
this.anti=anti;
}
final ThingsAndAntis anti;
ThingsAndAntis getAnti() { return anti; }
}
 
J

Joshua Cranmer

Andreas said:
It would be possible to have the compiler reserve space for all the
enum-consts, and only then run each's constructor. This would still
not yet enable cyclic *use*, but cyclic *referencing*:

This is what I referred to under the heading of "double-indirection."
The JLS stipulates that enum constants are instances of the enum and
therefore Object. Either you would have to give special consideration to
enums in the instruction set (a very tall order, as the largest change I
recall happening to the instruction set in Java 5 was the ability to use
class classinfo objects with ldc), or you would have to separate memory
reservation and initialization. Such a static initializer would look
like this:

new ThingsAndAntis
setstatic ThingsAndAntis.FOO
new ThingsAndAntis
setstatic ThingsAndAntis.BAR
new ThingsAndAntis
setstatic ThingsAndAntis.X
new ThingsAndAntis
setstatic ThingsAndAntis.Y

getstatic ThingsAndAntis.FOO
getstatic ThingsAndAntis.BAR
invokespecial <init>(LThingsAndAntis;)V
getstatic ThingsAndAntis.BAR
getstatic ThingsAndAntis.FOO
invokespecial <init>(LThingsAndAntis;)V
getstatic ThingsAndAntis.X
getstatic ThingsAndAntis.Y
invokespecial <init>(LThingsAndAntis;)V
getstatic ThingsAndAntis.Y
getstatic ThingsAndAntis.X
invokespecial <init>(LThingsAndAntis;)V

In principle, that much is doable, but you run into the same problems I
mentioned before: you limit what you can use in the constructor.
 

Members online

Forum statistics

Threads
473,982
Messages
2,570,185
Members
46,736
Latest member
AdolphBig6

Latest Threads

Top