Letters to the Editor

Readers debate the value of enums, propose an additional advantage to EJB, and comment on exception-handling practices

Tips 'N Tricks

"Java Tip 122: Beware of Java typesafe enumerations"

Vladimir Roubtsov

Enums should be encouraged, not discouraged

Vladimir,

It's true that you can't rely on Java enums to safely restrict the number of instances per-enum value to 1 when multiple classloaders are present. But you can still implement equals() and hashcode() with value semantics (rather than reference). Although that won't help speed, I find the pattern is still a better solution than using integer constants.

At the very least, the enum idiom makes code more readable and helps encapsulate conditional code on the enum value. The enum idiom should be encouraged rather than discouraged.

Edoardo

Edoardo, Your suggestion for fixing the typesafe pattern when multiple classloaders are present will not work as you describe. An important point that I tried to make in the article was that multiple classloaders do not simply mess with the number of instances; they mess with the class identity itself. Consider the case of fixing

equals()

with value semantics, as you suggest. To accomplish that, you must cast the

Object

parameter passed into

equals()

to

Enum

and retrieve some identifying value. However, if

Enum

is loaded twice, as simulated in my

Main

class, you are in fact dealing with two

different

classes,

Enum#1

and

Enum#2

(although with the same stringified name

Enum

).

this

will be of type

Enum#1

, and the method parameter will be of type

Enum#2

. Because of that, the cast will never succeed. In fact, you need to guard the cast with an instance-of check more or less like this:

public boolean equals (Object obj)
{
        if (obj instanceof Enum)
                return ((Enum) obj).getID() == this.getID(); // assuming 
getID() returns a primitive type of some kind
        else
                        return false;
}

This instance-of check will always fail because the

Enum

operand will always mean

Enum#1

. Download the sources for my tip (

http://images.techhive.com/downloads/idge/imported/article/jvw/2002/01/javatip122.zip

) and switch

EnumConsumer.validate()

to use

equals()

, instead of

==

. Then provide an implementation of

equals()

in the

Enum

class that would make both

validate()

calls in

Main.main()

work -- you will find that impossible. In the second

validate()

call,

this

and

obj

are instances of two different classes -- albeit with the same name. Your suggestion to use

hashCode()

will indeed work around the need to cast, because that method is available on the root

Object

class. However, this suggestion is not viable for a different reason: if two objects are identical or equal, their hash codes are the same, but the reverse is not guaranteed to be true. Thus, inferring that two objects are the same just because their hash codes are equal is incorrect in principle. I don't discourage this pattern's use simply because of speed issues: in some situations it

cannot

be made to work at all, regardless of whether you use

==

or

equals()

. Vladimir Roubtsov

Vladimir,

It's true that with multiple classloaders, we effectively have multiple definitions of the Enum class that can't be casted on each other.

I still maintain that enums prove useful for factoring conditional behavior on the enum value. I suggest the following:

  1. Use the enum pattern with regular value object implementations of equals() and hashcode(), if you don't anticipate comparing enum instances loaded by multiple classloaders.
  2. If you need to compare enums loaded by multiple classloaders, implement value object equals() and hashcode() with reflection. It will work.

You say:

In some situations it cannot be made to work at all, regardless of whether you use == or equals().

I disagree. It can work, but it will cost you speed. I always favor design over speed and revert to hacks -- like using integer constants instead of enums -- in critical code sections.

Edoardo

Edoardo, You are entitled to your own opinion, of course, but let me make some final comments. Let's again consider the original motivation for the typesafe enum construct. To me, the construct adds two advantages over the traditional integer sets:

  1. All comparisons are typesafe, which is ensured at compile time.
  2. If identity operator is okay to use, the comparisons remain very fast.

Now consider what happens when we use your fix suggestion:

  • equals() takes in a generic Object. Thus, the type safety is gone. The compiler will never catch that e.equals(SomeOtherEnum.TRUE) should be e.equals(Enum.TRUE). How is that better than using plain integers?
  • In fact, any suggestion to use a value-based comparison to fix the issues I raise would feature the same problem -- no type safety -- since a value-based comparison is essentially the same as comparing integer values in the first place!
  • In one breath, you say you favor design over speed and in another, you suggest using a class with equals() patched to use reflection. That appears incongruous to me, as reflection is a low-level, non-object-oriented feature of Java. It reduces compile-time checks and creates maintenance problems.
  • Even if we decide it's okay to use reflection in equals(), consider the ramifications: Remember that the two classes on both sides of equals() are still distinct in the case of multiple classloaders. Thus, to use reflection, you must call Class.getField() (you can't cache this, because a Field is specific to a Class), followed by Field.setAccessible() (unless you make the field public), followed by Field.get(). I disagree that the resulting monstrosity is better and more maintainable then an integer enumeration.
  • While speed might not always prove as important as good design, speed certainly matters to many people. Consider this example: the tag enumeration in javax.swing.text.html.HTML.Tag is used if, for example, you write your own HTML parser based on javax.swing.text.html.parser.ParserDelegator. When you process the callback events, you must compare against various HTML.Tag tags. In JDK 1.3, there are 74 such tag values. Imagine the worst case scenario: since you can't use a switch anymore, you have to use a chain of if/else if equals() comparisons. By the time you get to the last branch, not only will more than 70 methods be called, the methods all use slow reflection. The result: one slow parser.

My point: a typesafe enum class that uses

equals()

is slow and not typesafe -- so it loses the two advantages we started out with. Vladimir Roubtsov

Does J2EE violate fundamental Java concepts?

Vladimir,

The use of multiple classloaders breaking primitive language patterns brings the Java language to a crisis. Is J2EE (Java 2 Platform, Enterprise Edition) and multiple application classloaders so important that now fundamental language concepts are invalidated? Multiple application classloaders are generally evil as this article clearly substantiates. With them, you no longer can rely on any Singleton or instance identity patterns. Every expert Java book written today discusses the importance of instance identity for enumeration types. Now we have a whole fleet of application programmers who code to this pattern.

Is it any wonder that Java applications on J2EE are failing mysteriously and, in fact, are more prone to runtime failure? Should the J2EE patterns invisibly break the primitive language patterns experts have been advocating for years?

Lane Sharman

Lane, In all fairness, I must say that the multiple classloader situation I created in my article is not always likely to occur. Only a complicated set of circumstances would cause that situation to happen -- but when it does, I'd rather not be in charge of tracking it down. When writing application code, I would rather know that a certain problem can never happen, instead of almost never. When designing a class, you cannot always easily predict all contexts in which it will be used later. Perhaps we should lobby for introduction of a simple C/C++-like enum feature into Java? Vladimir Roubtsov

"To EJB, or not to EJB"

Humphrey Sheil

Don't forget one last EJB advantage

Humphrey,

Another major advantage of EJB: non-Java components can call EJB (Enterprise JavaBean) components.

Bharat Nagwani

Bharat, I didn't list this as an advantage of EJB per se, as it is more an advantage of the Java platform, as opposed to EJB technology. However, you are correct. A major advantage of the J2EE (Java 2 Platform, Enterprise Edition)/Java platform is that by using CORBA orbs, JNI (Java Native Interface), and so on, you can achieve almost any level of integration. However, with integration, you must consider specific architectural issues, such as performance and scalability (and with JNI - security and stability). Humphrey Sheil

"XSLT blooms with Java"

Taylor Cowan

Are Java extensions truly beneficial?

Taylor,

I really liked your article, which, by coincidence, came a few days after I discovered this facility myself. However, I think you might be encouraging overuse of Java extensions.

For one thing, a stylesheet shouldn't depend on a particular processor. The same stylesheet should be useful when processed by Xalan-Java or Xalan C++. Once you stick Java code in there, you must guarantee Xalan-Java use.

Secondly, a neat XSL (Extensible Stylesheet Language) solution to the problem might exist. A quick rush to use a procedural-based language will only create unreadable and fragile code. I agree that the declarative nature of XSLT (Extensible Stylesheet Language Transformations) can be daunting to us procedural-types, but it is very powerful once you get used to it. To conclude, this extension facility should be treated like JNI (Java Native Inteface): use with caution only in cases of extreme need.

Shimon Crown

Shimon, I agree in part, however, I do not believe that using extensions necessarily creates unreadable or fragile code. We're not even

inlining

Java, we're simply calling a function. One example that I had wanted to use was a tokenizing extension; since Xalan already provided one, I took to writing an original extension. The XSLT code, which

tokenizes

a string into a set of nodes would be infinitely more unreadable and fragile than just simply using Java's tokenizing ability. Just an extension library's existence adds strength to the idea that extensions are necessary from time to time. Ideally, these extensions will be wrapped back into the XSLT specification. XSLT by nature is unreadable by itself; it just couldn't be worse. XSLT doesn't even have if/then statements. (Yes, I know you can simulate them, but so what? They're still missing.) The current spec doesn't include regular expressions either. You say:

Once you stick Java code in there, you must guarantee Xalan-Java use.

Yes, you're right, but I'm a Java developer. I use XSLT as a utility in my Java applications. XSLT to the Java developer is secondary. We develop in Java because Java will run anywhere. Because Java will run anywhere, so will Xalan. Your JNI comment really gets to the point of our differences: I see XSLT and Java as similar to JSP (JavaServer Pages) and Java. A stylesheet with a lot of HTML content resembles a JSP page. I can replace my JSPs with XSLT. I'm talking J2EE (Java 2 Platform, Enterprise Edition), Java, and Web servers. That's my perspective as a developer, so in that sense, XSLT purity is not my goal. One thing to keep in mind is the stylesheet's purpose. Normally, my stylesheets run on the server within a J2EE application server. They play a part in a larger Java environment and will never, ever be used anywhere else. Perhaps I am encouraging overuse of Java extensions, but I'm really just trying to share a bit of joy I experienced recently. I agree that you can do almost anything with XSLT. However, I solved a bad problem in minutes once I figured out extensions. Taylor Cowan

"UI design with Tiles and Struts"

Prakash Malani

How do you incorporate a visual mode with Struts and Tiles?

Prakish,

With this approach, I wonder how a graphical designer can make a design for the pages you describe. This question comes up often in our projects: designers use software like Dreamweaver to create HTML and want (and need) to work visually (WYSIWYG mode). Design becomes a big challenge when the visual mode is an additional requirement for frameworks like Struts and Tiles. Perhaps we just need an update for Dreamweaver.

Marcel Offermans

Marcel, I have run into this problem again and again. Depending on your circumstances and project, the following options might be appropriate:

  • Encourage the UI designers to think in terms of view components, i.e., Tiles. This takes some learning and time. The first cut of the pages might feature duplication and heavy cohesion between components. Developers can component-ize, i.e., Tile-ize, the application. Now it will be hard to work in a WYSIWYG environment. A simple, yet highly effective solution, sets up a live environment for the UI designer, so they can observe real-time changes.
  • I have worked with many developers who are artistic and proficient in UI design and implementation. Once the initial cut is made to create the view components, the developers end up doing all the work from then on. In certain circumstances, they might seek help from the original UI designers.
  • I haven't played with any fancy tools. However, I imagine that tools like UltraDev and others should make things much easier in the future.

Prakash Malani

"Exceptional Practices"

Part 1. Use exceptions effectively in your programs

Part 2. Use exception chaining to preserve debugging information

Part 3. Use message catalogs for easy localization

Brian Goetz

More exceptional practices

Brian,

Thanks for a tremendous series on exception handling. As a lead developer, I have asked all my junior developers to read this series. Too often, exception-handling by even experienced developers is ignored or misused.

I have some comments about Part 3: You should have stressed more the use of properties files to hold the message catalog, as this approach truly separates the text from the Java code -- as opposed to the messages being in a ListResourceBundle.

Secondly, I have discovered two extreme views with exceptions: One extreme is the developer who maintains an almost flat exception hierarchy and then has many messages in the message catalog. While this means fewer classes to develop, often the client code starts having to peek at the message key to determine the appropriate action to take. On the other extreme is the developer who wants an extremely large hierarchy and feels that the type (class) of the message itself should indicate all message meaning. This tends to lead to an explosion of exception classes. There is a middle ground that features enough of a hierarchy where client code does not have to use the message key to take action -- of course, you can't always know how an API will be used.

Bill Siggelkow

Bill, I tried to avoid the topic of where the messages live -- you could use

ListResourceBundle

or

PropertyResourceBundle

-- the effect is the same. While

ListResourceBundle

s are still classes, they contain nothing but resource strings, so the

ListResourceBundle

source files can be owned by the document writer -- and that's the important thing. Actually, the two exception views are pretty much orthogonal to the question of whether or not you have to peek at the message text/key. Whether you have a flat hierarchy or a deeply branching hierarchy, the real question is whether you have enough information embedded in the type to identify the exception, which basically translates to "How lazy were you about making enough exception classes?" One technique I've used, which some dislike, is to group related exception classes as static subclasses:

public class Holder {
   public static class FooException extends Exception {
   }
   public static class BarException extends Exception {
   }
}

You now have the ability to put related code in the same file. Brian Goetz