This week's giveaway is in the EJB and other Java EE Technologies forum.
We're giving away four copies of EJB 3 in Action and have Debu Panda, Reza Rahman, Ryan Cuprak, and Michael Remijan on-line!
See this thread for details.
The moose likes Java in General and the fly likes Enum , providing getInstance() method Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login


Win a copy of EJB 3 in Action this week in the EJB and other Java EE Technologies forum!
JavaRanch » Java Forums » Java » Java in General
Bookmark "Enum , providing getInstance() method" Watch "Enum , providing getInstance() method" New topic
Author

Enum , providing getInstance() method

Sonny Gill
Ranch Hand

Joined: Feb 02, 2002
Posts: 1211

Hi all. Ok...tinkering with new JDK features..

Since, enum(s) should have a limited number of instances, does it make sense to provide a (Singleton'ish) getInstance method ?



This method could be useful where the enum instance used depends on a char value in the database for example.

Is there a better way of doing this?

Thanks.
Tony Morris
Ranch Hand

Joined: Sep 24, 2003
Posts: 1608
You might mean a reverse mapping, and it makes no sense; your example reverse mapping is specific to your enum type (not all enums have a reverse mapping from a char to the enum type). Also, reverse mappings should be provided with O(1) lookup, not O(n) that is provided by switch/case (one of two fundamental reasons why switch/case should never be used).


Tony Morris
Java Q&A (FAQ, Trivia)
Sonny Gill
Ranch Hand

Joined: Feb 02, 2002
Posts: 1211

Thanks Tony.

The enum type to be used is based on a value of type CHAR(1) in the database.
What is the alternative?
Tony Morris
Ranch Hand

Joined: Sep 24, 2003
Posts: 1608
There are a few gotchyas when implementing a reverse mapping with both 1.5 enums and the appropriate alternative prior to 1.5 (which unfortunately is misconceived as a multitude of int or Strings in an interface or class).

Typically, I define a private static class of the enum that contains a Map ,for your case, Map<Character, EnumType>, and lazily initialise it (in a static initialiser for class loader) to the appropriate values (or load them from an external resource if appropriate (the other reason why switch/case is flawed - you cannot do this)).

There is an 'almost example' in JTiger - the difference being it's not a reverse mapping for an enum, but is a public interface (for your enum, it would all be private exception for your public 'getInstance' method). The example is the class org.jtiger.report.html.ResultMappingFactory. This class is included with the source code, which is in the download.

Good luck.
[ July 05, 2005: Message edited by: Tony Morris ]
Jim Yingst
Wanderer
Sheriff

Joined: Jan 30, 2000
Posts: 18671
[Tony]: You might mean a reverse mapping, and it makes no sense; your example reverse mapping is specific to your enum type (not all enums have a reverse mapping from a char to the enum type).

It's true that you can't do this particular type of mapping in general for all enums. However it certainly can be done for this particular mapping. "It makes no sense" makes no sense here, IMO.

In fact, enums already have a reverse lookup built in - the valueOf() method defined in java.lang.Enum. It uses Strings - specifically, it uses the identifiers for the specific values of the enum. So Sonny could do something like this:

Instances can then be looked up easily:

The downside of this is that it forces us to use identifiers which duplicate the database values. This results in a loss of readability if we would have preferred a name like North rather than N. It may also require us to abandon conventional Java naming conventions in order to match the DB values. Still, it may be acceptable to put up with these problems, as it's extremely quick and easy to create the enum in this case - no methods need to be coded at all.

Also, reverse mappings should be provided with O(1) lookup, not O(n) that is provided by switch/case (one of two fundamental reasons why switch/case should never be used).

Actually switch statements are quite capable of providing O(1) lookup - that's what the tableswitch instruction in the JVM is for. Of course, the compiler doesn't bother using a tableswitch for low values of N like 4, as in Sonny's example. The overhead in setting up the table isn't warranted - it's faster to use a lookupswitch. Note that the docs for lookupswitch indicate that it may be faster than O(n), and indeed it's not difficult to imagine that a JVM could do the lookup in O(log(N)) for a lookupswitch, since it basically just requires a binary search. I don't know if the JVM actually does a binary search - maybe there's never been any significant demand for optimization in this area; dunno. (Switch statements aren't very common in Java, as there are usually better alternatives involving polymorphism and/or a Map of some sort.) Regardless, the compiler has several options for implementing switch statements efficiently, and overall it does a pretty good job in this department. There are some cases where it's prohibitive to use a tableswitch, if the ranch of values in the switch is so wide that it takes too much memory. I think such cases are usually pretty rare, in practice. And I suspect the performance devolves to O(log(N)) in such cases - which is not bad at all for most applications.

In summary: switch statements are generally very fast. If there are indeed real-world cases where performance is a valid reason not to use a switch statement - I'd love to hear the details.

Now, as I alluded previously: this doesn't mean I recommend switch statements in general. My main objection to them is the error-prone syntax whcih requires us to explicitly provide a break. And if the possible values of the enum are changed in the future, I'd prefer to edit this in only one place (the list of values at the beginning of the enum declaration) without worrying about a separate switch statement elsewhere in the code. However - using a switch statement is not the end of the world, and Sonny's example seems fairly reasonable to me.

Typically, I define a private static class of the enum that contains a Map ,for your case, Map<Character, EnumType>, and lazily initialise it (in a static initialiser for class loader) to the appropriate values (or load them from an external resource if appropriate (the other reason why switch/case is flawed - you cannot do this)).

No disagreements on this section - this would usually be my preferred strategy as well.
[ July 05, 2005: Message edited by: Jim Yingst ]

"I'm not back." - Bill Harding, Twister
Sonny Gill
Ranch Hand

Joined: Feb 02, 2002
Posts: 1211

Thanks Tony and Jim.
Very informative posts. Jim thanks for backing me on 'it making sense' he he

I read up a bit on Enum before I made the post, and was aware of the valueOf method. But having the method dictate the instance names felt a bit clunky. The private static class option you both suggest is much more intersting.

That is quite a bit of information in the two posts, I am going to read up more and experiment before implementing it.

Thanks again.
Tony Morris
Ranch Hand

Joined: Sep 24, 2003
Posts: 1608

Actually switch statements are quite capable of providing O(1) lookup - that's what the tableswitch instruction in the JVM is for.

Yes, I concede that I was over-simplifying, hoping that nobody on the forum would notice - good call anyway. Also, there is an implicit reverse mapping as you have noticed; the String representing the field name. Guess how many times the specification changed with respect to this aspect during alpha stages of release? I can recall several (in fact, that little handbook that people speak of quickly went out of date as a result).
Ilja Preuss
author
Sheriff

Joined: Jul 11, 2001
Posts: 14112


(This is using the Typesafe Enum pattern in Java 2 1.4 - I can't use Tiger yet, but it probably translates well to enums, doesn't it?)
[ July 05, 2005: Message edited by: Ilja Preuss ]

The soul is dyed the color of its thoughts. Think only on those things that are in line with your principles and can bear the light of day. The content of your character is your choice. Day by day, what you do is who you become. Your integrity is your destiny - it is the light that guides your way. - Heraclitus
Tony Morris
Ranch Hand

Joined: Sep 24, 2003
Posts: 1608
This is using the Typesafe Enum pattern in Java 2 1.4 - I can't use Tiger yet, but it probably translates well to enums, doesn't it?

Yes it translates precisely at compile-time to the same bytecode. For example, your private constructor needs no access modifier on a 1.5 enum (it's implicitly private), and the constructor argument is passed at the declaration of the enum field (which just compiles to being passed to the constructor).

In fact, the type-safe enum is what inspired 1.5 enums.
Ilja Preuss
author
Sheriff

Joined: Jul 11, 2001
Posts: 14112
Originally posted by Tony Morris:
In fact, the type-safe enum is what inspired 1.5 enums.


Ah yes. What I was concerned about is the static Map thing. Using the typesafe enum pattern, it needs to be declared before the enum constants, because otherwise it wouldn't be instanciated when the constructor gets called. Don't know how that works with Tiger enums - I guess if everything else fails you need some lazy initialization code inside the constructor...
Tony Morris
Ranch Hand

Joined: Sep 24, 2003
Posts: 1608
Yes, that's one of the 'gotchyas', and also why I declare it as a nested class of the enum and that Map is 'lazily' populated (when that nested class is loaded implying that the enum class has already been loaded).
 
I agree. Here's the link: http://aspose.com/file-tools
 
subject: Enum , providing getInstance() method
 
Similar Threads
few scenarios in JAVA where instinct doesn'n play right
I need your help
JTextField
Hoe can i make the rat to move?
Java Recursion method