Letters to the Editor
JavaWorld.com, 02/28/03
- Digg
- Reddit
- SlashDot
- Stumble
- del.icio.us
- Technorati
- dzone
Tips 'N Tricks
"Java Tip 133: More on Typesafe Enums"
Philip Bishop
EnumSyntax can provide a solution for integer-based enums
Phillip,
It seems like J2SE (Java 2 Platform, Standard Edition) 1.4 supplies EnumSyntax as a generic base class for enumeration types. It implements readResolve() so you don't have to and implements Serializable and Cloneable. This seems like a good base for application specific enums—I don't know why Sun Microsystems stuck it in the print package
instead of java.util:
"Class EnumSyntax is an abstract base class providing the common implementation of all 'typesafe enumeration' objects." (From javax.print.attribute.EnumSyntax.)
Andrew Wason
Andrew, Thank you for bringing the EnumSyntax class to my attention. Yes, it is an obscure package, one that I haven't had the pleasure of visiting. As you point out,
the EnumSyntax class looks useful for integer-based typesafe constants, however, using the EnumSyntax class does mean you must provide integer values for each field in the subclass, something that can be prone to duplicates
occurring due to cut and paste or programming errors. With non-value-based constants, these types of errors cannot occur.
Ultimately, it comes down to what developers want out of a typesafe enum/constant—if they don't need to use the underlying
integer or string then a non-value-based constant (like my first NumberConstant example) will do. On the other hand, as you say, the EnumSyntax class can provide a simple solution for integer-based enums. Philip Bishop
Another approach: Keep a field semantically equivalent to the type
Philip,
I'm a big fan of the typesafe enumeration too; I have been since my C++ days.
One thing I like to do is keep in the class a field semantically equivalent to the type I'm trying to describe. In your NumberConstants example, it could be an integer, other times a string. This field is initialized with the constructor.
Then, if you override equals(), hashcode(), and compareTo(), you have a class that deals well with serialization. Unless I'm working with primitives, I try to avoid comparisons using
the == operator at all costs.
Gareth Mahon
Gareth, I generally agree that the technique you describe is a good solution to the serialization problem and also allows
the enums to be used with collections. However, cases exist where you might simply want to use a typesafe enum in a distributed
system and not bother overriding equals(), hashcode(), and so on—this is why I came up with the AbstractConstant class. In fact, AbstractConstant emerged while I was working with Jini and JavaSpaces, which have different serialization semantics. In Entry classes, each field is serialized separately, instead of the whole object. Initially I used value-based enums (along the
lines you describe) so that the Entry objects containing enums could be matchable, but this became tedious. So I tried to come up with a simple and reusable solution—hence
the birth of AbstractConstant. Philip Bishop
Java Q&A
"Got Resources?"
Vladimir Roubtsov
Can you mix getBundle() with getResource()?
Vladimir,
I learned that a.package.name.SomeClassName.class.getResource("name.properties") is better than SomeClassName.class.getClassLoader().getResource("a/package/name/name.properties") because you eliminate duplicate code.
I wonder if this can be applied to load resource bundles. As far as I know, you have to use ResourceBundle.getBundle("a/package/name/name",locale) to load the "a/package/name/name.properties" resource bundle, but this method suffers from the same drawback.
Dieter Cailliau
Dieter, I can say that ResourceBundle does not provide package-relative lookup like Class.getResource(). Technically, it is possible even though getBundle() is a static method, but Sun Microsystems simply did not implement it like that. Unfortunately, Java does not have API uniformity
in this area. Also, getBundle() uses dots for package name delimiters, not slashes, so the code will actually read ResourceBundle.getBundle("a.package.name.name",locale). This different naming convention is another reason why getBundle() cannot be mixed easily with getResource() if the resource names come from system properties, other property files, and so on. Vladimir Roubtsov
Java Q&A
"My Kingdom for a Good Timer!"
Vladimir Roubtsov
How do you accurately profile small code segments with repeatable results?
Vladimir,
At the end of your article, you comment that the CPU time measured by your method is wall-clock time as opposed to true CPU
usage. In your experience, how repeatable are the results from this method (What is the average variance due to OS noise?)?
We are trying to find a way to do performance analysis on some automatically generated test code (basic asserts and precondition
checking) to see how much it affects the runtime. Theoretically, that will be a small number sometimes—on the order of a few
microseconds. I have some other options available, such as looping the test code repeatedly to consume enough time for the
JVMPI (JVM Profiler Interface) to notice, but that still leaves me with an inaccuracy of plus or minus one microsecond.
Given your experience, what would you say is the most accurate way to profile small Java code segments with repeatable results?
Joseph Ruscio
Joseph, I must point out that if a Java code segment is short enough, even a solution like the one in my article becomes inadequate,
due to the unavoidable native method call overhead (which has been mounting with increasing JDK versions). The only workable
approach that remains is to have direct JVM support implemented with little overhead—basically, something like JVMPI, with
high resolution timing or direct counting of byte code/native instructions. That approach is within the abilities of an individual
or a small team of developers, especially if it is done once for a research project. However, the same approach would prove
time-consuming for a commercial software company, especially since it would require staying in sync with Java releases. As
an example, groups within IBM that used an IBM JVM specially instrumented to measure RMI (remote method invocation) overhead
have published performance reports. Those reports used a custom-instrumented Java 1.1 JVM and were never reproduced with later
JVMs. In general, this approach is hard to follow for any commercial company that does not build their own JVMs. In my experience,
the results based on measuring wall-clock time have been quite reproducible. Of course, I would usually try to have my machine
just run the JVM process and nothing else (and have the JVM run just the code being profiled in a single thread, if possible).
This differs from your situation, where you probably measure overhead under normal execution circumstances. Another factor
that affects wall-clock time measurements is garbage collection activity. Sometimes, a measured data point will be outlaying,
as much as by several orders of magnitude. Even though such data points could be relatively rare, the extreme degree to which
they differ from normal data points can noticeably affect the statistical average. Because of that, I developed a habit of
collecting many data points and using, say, a 90 percent percentile as my final value and not relying on the traditional average.
In the end, because of the unpredictable nature of garbage collection interference, I prefer to use an API that provides true
CPU usage data. For your project, your approach seems the best, given the requirements. You might want to consider taking
Sun Microsystems' public sources for the HotSpot 1.4 JVM and rebuilding them with a high-resolution timer in their JVMPI implementation.
Vladimir Roubtsov
What about other JNI libraries?
- Digg
- Reddit
- SlashDot
- Stumble
- del.icio.us
- Technorati
- dzone