| |
(Editor's Note: Because this session will be repeated on Friday afternoon, we have purposely omitted details on the puzzles themselves, and focused primarily on the lessons learned, which are available in the companion book.)
One of the most popular sessions on Tuesday afternoon coincidentally had the longest name: TS- 2707: Java Puzzlers, Episode VI: The Phantom Reference Menace, Attack of the Clone, Revenge of the Shift. This popular session was presented by Joshua Bloch, Chief Java Architect with Google, Inc., and William Pugh, Professor, University of Maryland, and is based on an equally-popular book called Java Puzzlers, available from Addison Wesley in the conference bookstore.
This session has been standing-room-only in years past, and it's easy to see why: it offers a serious mental challenge for even the most adept Java coder. This year, the session jumped in with a rousing John Williams rendition of the Star Wars theme, including a title scroll that outlined just what the "space cadet brothers" were about to unleash on the unsuspecting audience.
The first puzzle was called "The Joys of Sets," and, just as in years past, most of the audience failed to guess the correct answer. Here, however, we learn two important points:
- A Short object may not equate to an Integer object that has the same value.
- The interface for Set allows you to add a Set of generic type
<E>, but you can remove any object, not just type <E>. This behavior is correct, and mandated by the specification.
As far as advice, Bloch and Pugh offer the following:
- Avoid mixing types; don't mix int's and short's
- In fact, there isn't really any need to use short's, so unless you have a really strong need, avoid them altogether.
The second puzzle, aptly titled "More Joys of Sets" has the user create HashMap keys that consist or several URL objects. Again, most of the audience was unable to guess the correct answer.
The important thing the audience learned here is that the URL object's equals() method is, in effect, broken. In this case, two URL objects are equal if they resolve to the same IP address and port, not just if they have equal strings. However, Bloch and Pugh point out an even more severe Achilles' Heel: the equality behavior differs depending on if you're connected to the network, where virtual addresses can resolve to the same host, or if you're not on the net, where the resolve is a blocking operation. So, as far as lessons learned, they recommend:
- Don't use URL; use URI instead. URI makes no attempt to compare addresses or ports. In addition, don't use URL as a Set element or a Map key.
- For API designers, the
equals() method should not depend on the environment. For example, in this case, equality should not change if a computer is connected to the Internet versus standalone.
The third puzzle, called "Racy Little Number", shows us that an exception thrown in a JUnit testing thread must be caught and somehow acknowledged, or JUnit will assume that a test passed. The lessons learned here include:
- A JUnit testing thread must catch both Error and Exception, and use another strategy to report the results.
- JUnit does not support concurrency. As a programmer, you must provide your own strategy for handling concurrent testing, otherwise you get a false sense of security. (If you are interested in concurrent testing frameworks, William Pugh will be announcing such a framework at this year's JavaOne conference in another session, "Testing Concurrent Software.")
The next puzzle, called "Elvis Lives Again", was a true mind bender, and dealt with the order of initialization with classes. Here we learn that auto-unboxing occurs when we least expect it. The lessons for this puzzle include:
- The compiler guarantees only that class initialization always occurs in textual order from top to bottom.
- Auto-unboxing an object that is null will throw a
NullPointerException.
- Watch out for circularities in class initialization.
In addition, Bloch and Push strongly warn against using the Boolean object as a three-valued return type, where in addition to true and false, programmers occasionally use null as an indicator that the method resulted in an error.
The fifth puzzle, "Mind the Gap", details an unusual behavior with the InputStream's skip() method. To summarize, programmers must always make a specific check to ensure that the method performed in the expect manner. Most of the time it will, but not always. In fact, says Bloch, there are 63 calls to skip() inside the JDK itself, and 56 of them are not checked.
Bloch and Pugh recommend wrapping the skip() method to create a method called skipFully(). This method consists of a while loop that guarantees the correct skip.
- The
skip() method is hard to use and error-prone.
- Use your own
skipFully() instead of skip.
- More generally, if an API method performs anomalously, wrap it
- For API designers: Don't violate the principles of "least astonishment." Make it easy to do simple things.
At this point, Joshua Bloch gave the audience a truly timeless line to remember: "APIs, like diamonds, are forever."
The next puzzle, called "Histogram Mystery", demonstrated something that most people would have never expected: Math.abs() is not guaranteed to return a positive number. In fact, in one rare case, thanks to a quirk in twos-complement arithmetic, the absolute value of the negative number is itself a negative number. In addition, the remainder operator (%) can also return a negative number. Nearly everyone in the audience failed to guess the correct answer.
The seventh puzzle, "A Sea of Troubles," shows us that the ? : operator has strange behavior when applied to mismatched types. In fact, the specification states a dizzying array of qualifications that are required for the ? : operator to work correctly. The lessons learned here are:
- Avoid the ? : operator in these cases.
- If in doubt, just use an if/else clause.
The final puzzle, called "Ground Round" told us to avoid unexpected widening, such as what occurs when an int is promoted to a float, or a long is promoted to a double. One important lesson to be learned here is that the float type is seldom called for; use double instead. In addition, method overloading is dangerous, particularly when combined with unexpected widening.
Finally, Josh Bloch and William Pugh offer this sage advice for programmers working with the Java platform:
- The Java platform is reasonably simple and elegant, but do your best to keep programs clear and simple.
- If you are not sure what a method does, it probably doesn't always do what you think it does.
- Use the FindBugs (
findbugs.sourceforge.net) tool, which checks for each of the behaviors cited here, as well as many others.
If you're curious as to the exact code that will generate these errors, don't miss the repeat session of Java Puzzlers on Friday afternoon at 2:50 PM.
|
|