I don't understand enums

P

Paul Tomblin

I have some existing code that uses the classic old

class Playlist
{
public static final int PLAYLISTTYPE_FEATURE = 1;
public static final int PLAYLISTTYPE_TRAILER = 2;
...

But now that we're using Java 1.5, I've got new code where I decided to
use a Java 1.5 enum:

public enum PlaylistType
{
FEATURE(1, "Feature"),
TRAILER(2, "Trailer"),
....

final int id;
final String name;

PlaylistType(final int id, final String name)
{
this.id = id;
this.name = name;
}

public int getID()
{
return id;
}

public String toString()
{
return name;
}
}

In order to make sure the new code and the old code work together, I
changed the definition of the old ints to
public static final int PLAYLISTTYPE_FEATURE = PlaylistType.FEATURE.getID();
public static final int PLAYLISTTYPE_TRAILER = PlaylistType.TRAILER.getID();

Note that they're still "public static final".

But now case statements that worked before the change are breaking with
Eclipse complaining that "case expressions must be constant expressions".
Huh? They sure look constant to me. How could it think that
PLAYLISTTYPE_FEATURE could change values when it's declared final?
 
R

Robert Klemme

I have some existing code that uses the classic old

class Playlist
{
public static final int PLAYLISTTYPE_FEATURE = 1;
public static final int PLAYLISTTYPE_TRAILER = 2;
...

But now that we're using Java 1.5, I've got new code where I decided to
use a Java 1.5 enum:

public enum PlaylistType
{
FEATURE(1, "Feature"),
TRAILER(2, "Trailer"),
...

final int id;
final String name;

PlaylistType(final int id, final String name)
{
this.id = id;
this.name = name;
}

public int getID()
{
return id;
}

public String toString()
{
return name;
}
}

In order to make sure the new code and the old code work together, I
changed the definition of the old ints to
public static final int PLAYLISTTYPE_FEATURE = PlaylistType.FEATURE.getID();
public static final int PLAYLISTTYPE_TRAILER = PlaylistType.TRAILER.getID();

Note that they're still "public static final".

But now case statements that worked before the change are breaking with
Eclipse complaining that "case expressions must be constant expressions".
Huh? They sure look constant to me. How could it think that
PLAYLISTTYPE_FEATURE could change values when it's declared final?

They are constant for one run of the program. But they might not
totally be constant. Eclipse cannot know that the return value of
getID() will never change. That's why. You probably should do yourself
a favour and do a global search and replace from the old constants to
the new constants. OR you drop the old constants and reuse the names.

Btw, id is not necessary, there is Enum.ordinal(). And if you do not
insist on the case of the name then you also do not need the name parameter.

Here's what I'd probably do

// untested
class PlayList
public static enum Type {
Feature,
Trailer,
// more to come
}

// other members
end

Note that you can use enum values directly in switch statements. There
is usually no need for access to the ordinal.

Kind regards

robert
 
L

Lasse Reichstein Nielsen

(e-mail address removed) (Paul Tomblin) writes:

public static final int PLAYLISTTYPE_FEATURE = PlaylistType.FEATURE.getID(); ....
Note that they're still "public static final".

But now case statements that worked before the change are breaking with
Eclipse complaining that "case expressions must be constant expressions".
Huh? They sure look constant to me.

They are not "constant expressions" in the sense of the Java specification,
which means *compile time* constants. What you have is a method call, on
an object from another class. There is no way to know what that might
return when it's executed (remember, you might recompile PlaylistType
and not Playlist, and it should still work).
How could it think that
PLAYLISTTYPE_FEATURE could change values when it's declared final?

It's not that it can't change during one execution of the program,
but that it's no a compile time constant that can be inlined in the
switch statement implementation.

However, you can switch on enums too, so change the switch to do
switch (playlistType) {
case PlaylistType.FEATURE: // ...
case PlaylistType.TRAILER: // ...
}

(The compiler will, internally, assign a compile time constant to each
enum value and do the switch on that instead.)

/L
 
P

Paul Tomblin

In a previous article said:
They are constant for one run of the program. But they might not
totally be constant. Eclipse cannot know that the return value of
getID() will never change. That's why. You probably should do yourself

Ah, the light dawns.
Btw, id is not necessary, there is Enum.ordinal(). And if you do not

Except I absolutely have to make sure my numbers agree with what is stored
in the database. I was worried that somebody might decide they'd look
niceer if they were in alphabetical order or something and change the
ordinals, which probably wouldn't be discovered until some customer
discovered that his new content works but his old content doesn't.
insist on the case of the name then you also do not need the name parameter.

Does toString() give you the enum's name then?
 
R

Robert Klemme

Ah, the light dawns.


Except I absolutely have to make sure my numbers agree with what is stored
in the database. I was worried that somebody might decide they'd look
niceer if they were in alphabetical order or something and change the
ordinals, which probably wouldn't be discovered until some customer
discovered that his new content works but his old content doesn't.

Well, in that case. You should probably check with the JLS, but I
believe the order in the source file determines the order of ordinal().
Does toString() give you the enum's name then?

Yep, same value as name().

Cheers

robert
 
P

Patricia Shanahan

Robert said:
Well, in that case. You should probably check with the JLS, but I
believe the order in the source file determines the order of ordinal().

It's documented in the Enum class API documentation for ordinal() at
http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Enum.html#ordinal()

Have you considered treating the database field value as distinct from
the enum ordinal, and providing an explicit mapping between them? That
might be less fragile, and allow you to insert new values at logical
positions in the ordinal.

Patricia
 
P

Paul Tomblin

In a previous article said:
Have you considered treating the database field value as distinct from
the enum ordinal, and providing an explicit mapping between them? That
might be less fragile, and allow you to insert new values at logical
positions in the ordinal.

That's the whole point of the id field and getID() method.
 
W

Wojtek

Paul Tomblin wrote :
Except I absolutely have to make sure my numbers agree with what is stored
in the database.

Use privagte statiucs to hold the values you want no one to alter

public enum PlaylistType
{
// do not change, only add
private static final int VAL_FEATURE = 1;
private static final int VAL_TRAILER = 2;
// do not change, only add

FEATURE(VAL_FEATURE, "Feature"),
TRAILER(VAL_TRAILER, "Trailer"),
....

final int id;
final String name;

PlaylistType(final int id, final String name)
{
this.id = id;
this.name = name;
}

public int getID()
{
return id;
}

public String toString()
{
return name;
}
}
 
R

Robert Klemme

That's the whole point of the id field and getID() method.

I completely agree with Patricia: use an explicit mapping, probably a
pair of static methods somewhere. If you can implement that mapping via
ordinal() or some other attribute you put in the enum class then is just
a matter of implementation. But you gain a lot of robustness because
you decouple DB representation from application representation.

Kind regards

robert
 
L

Lasse Reichstein Nielsen

Lew said:
Actually, you have to drop the type from the case label, so it would just be
case FEATURE:
etc.

Woot, I learned something today :)
Indeed, the only valid value of a switch label for an enum type is
an enum constant's name - a single identifier.

/L
 
P

Patricia Shanahan

Paul said:
That's the whole point of the id field and getID() method.

I was thinking more explicit and centralized than that, something like a
single master map that has database id (as an Integer) as key and enum
element as value, or the other way round, with methods to get the id
given the enum, and to get the enum given the id.

That way, it would be really easy to make sure existing database id
values are mapped to the right enum element, and that a new enum element
is assigned a previously unused database id.

Patricia
 
R

Roedy Green

Have you considered treating the database field value as distinct from
the enum ordinal, and providing an explicit mapping between them? That
might be less fragile, and allow you to insert new values at logical
positions in the ordinal.

I go on at great length about this fragility problem and various
indecent and decent ways around it.

see http://mindprod.com/jgloss/enum.html#DATABASES
 
D

Daniel Pitts

Roedy said:
I go on at great length about this fragility problem and various
indecent and decent ways around it.

see http://mindprod.com/jgloss/enum.html#DATABASES
Often, enums in databases shouldn't be thought of in the same way as
Java enums.

One approach I take is to create a table that contains the enum ID and
the enum name, then use a FK into that table. In java, I can have a
mapping between the name and the Java enum value. Or, I could do
without enums altogether in the Java code (enums are useful, but also
often abuseful).
 

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

Members online

Forum statistics

Threads
473,968
Messages
2,570,152
Members
46,698
Latest member
LydiaHalle

Latest Threads

Top