New enums, how to they work

R

Roedy Green

According to Joshua Bloch, the new enums have the following
properties:


They provide compile-time type safety--int enums don't provide any
type safety at all.

They provide a proper name space for the enumerated type--with int
enums you have to prefix the constants to get any semblance of a name
space.

They're robust--int enums are compiled into clients, and you have to
recompile clients if you add, remove, or reorder constants.
Printed values are informative--if you print an int enum you just see
a number.

Because they're objects, you can put them in collections.
Because they're essentially classes, you can add arbitrary fields and
methods.

This is BETTER than I hoped for. I thought for sure Sun would
sacrifice the ability to use the new enums in case clauses, making
them useless for much of the stuff I do.

What puzzles me is how did they pull this off. Sun went to great
lengths not to change the JVM to do generics. What compiles the
lookupswitch and tableswitch byte code at run time from the latest
enum definition? Enums must somehow look like ordinary static
constants. Yet they are not resolved at compile time.
 
T

Tim Tyler

[enums in case clauses]

: What puzzles me is how did they pull this off. Sun went to great
: lengths not to change the JVM to do generics. What compiles the
: lookupswitch and tableswitch byte code at run time from the latest
: enum definition?

``VI. Implementation Notes

Within the same compilation unit containing an enum class
declaration, switch statements on enums can easily be
compiled down to ordinary swtich statements, as it is
guaranteed that no constants will be added, removed, or
reordered from the enum class after the switch statement is
compiled. Thus, the ordinals implied by the declarations are
guaranteed to be correct at run time.

Outside of the compilation unit, there are at least two
choices. (1) compile the switch statement down to a
"multiway if-statement":

if (<expr>.ordinal == Color.red.ordinal) {
...
} else if (<expr>.ordinal == Color.green.ordinal) {
...
} else if (<expr>.ordinal == Color.blue.ordinal) {
...
} else {
... // default case
}

The .ordinal after each enum expression is superfluous, but
enables a JVM compiler optimization, wherein the JVM
compiler recognizes at class initialization time that a case
statement may be substituted for the if-statement. Hotspot
does not yet contain this optimization, but could be
modified to do so.

An intriguing alternative that might offer similar
performance with no support from the JVM is suggested by the
old saw that every problem in Computer Science may be solved
with an extra layer of indirection. Each switch statement
outside of the compilation unit containing the enum class
declaration will compile down to a switch statement on an
array reference. The array will be initialized lazily:

private static class $whatever {
static int[] permutation = new int[Color.VALUES.size()];
static {
permutation[Color.red.ordinal] = 1;
permutation[Color.blue.ordinal] = 2;
permutation[Color.green.ordinal] = 3;
}
}

switch($whatever.permutation[<expr>.ordinal]) {
case 1: // red
...
case 2: // blue
...
case 3: // green
...
default:
...
}

This approach has higher "fixed cost" per case-statement and
higher footprint, but low cost per execution of the cast
statement.''

- http://jcp.org/aboutJava/communityprocess/jsr/tiger/enum.html
 
X

xarax

Roedy Green said:
According to Joshua Bloch, the new enums have the following
properties:


They provide compile-time type safety--int enums don't provide any
type safety at all.

They provide a proper name space for the enumerated type--with int
enums you have to prefix the constants to get any semblance of a name
space.

They're robust--int enums are compiled into clients, and you have to
recompile clients if you add, remove, or reorder constants.
Printed values are informative--if you print an int enum you just see
a number.

Because they're objects, you can put them in collections.
Because they're essentially classes, you can add arbitrary fields and
methods.

This is BETTER than I hoped for. I thought for sure Sun would
sacrifice the ability to use the new enums in case clauses, making
them useless for much of the stuff I do.

What puzzles me is how did they pull this off. Sun went to great
lengths not to change the JVM to do generics. What compiles the
lookupswitch and tableswitch byte code at run time from the latest
enum definition? Enums must somehow look like ordinary static
constants. Yet they are not resolved at compile time.

The document at

http://jcp.org/aboutJava/communityprocess/jsr/tiger/enum.html


gives a good overview of the Enum thing. In a nutshell, the compiler
is transforming an Enum specification into an equivalent class where
each enum member is implemented by the compiler as an anonymous inner
class.

If a case label of a switch statement refers to an enum class
that is defined in the same compilation unit, then the compiler will
substitute the ordinary int value for that enum (just like it does
for ordinary "static final int" members with self-defining constant
expressions). For a separate compilation unit, the compiler converts
the switch block into an extended if-else if- statement that compares
the subject value with each case value (using reference equality ==).

The document also suggests that the compiler may be able to use
an array reference to translate an enum value into an ordinary
integer that can subsequently be used in an ordinary switch block.

However, enum members can be assigned arbitrary constant values,
including negative values and very large values. Allocating a single
flat array for translation is unworkable, unless you know the
range of the enums (which defeats a major benefit of enums -- not
requiring the programmer to know such details of the enum type).

Therefore, I would suggest that any use of enum values as case
labels in an external compilation unit be viewed with caution. The
case labels should be ordered by frequency; the most frequently
occuring value should be first and the least frequently occuring
value should be the last case label. The programmer doesn't need
to know the values of the enums, only which enums occur more
frequently than others.

Generally speaking, a switch{} block that is used to determine the
type of a "thing" may be better implemented using polymorphism when
the "thing" can be represented as a reference type. If the "thing"
cannot be easily represented as an object (like it's a raw data packet
coming over a TCP/IP stream with a numeric identifier for the
packet type), then there should be only one place in the entire
application that knows how to distinguish such things with its own
switch{} block. In that place is where you define the Enum class so
that its member values can be hard-compiled into the switch{} block.

2 cents worth. Your mileage may vary.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads

Serialising Enums 11
Smuggling information to enums 22
enums and signed/unsigned 4
new stuff in 1.8 14
generics & enums 1
Bitwise (flags) enums: how to compare? 6
Java 1.5 Enums 7
Loving Enums 2

Members online

No members online now.

Forum statistics

Threads
473,774
Messages
2,569,596
Members
45,130
Latest member
MitchellTe
Top