August 26, 2004
I'm sitting at the Denver JUG meeting and Joshua
Bloch and Neal Gafter
just finished a talk on "Java Puzzlers". I didn't show up until halfway
through - but it was still a great half hour. They had a bunch of slides with
problems that had seemingly easy answers. They'd both have a good dialog about
their proposed answers - and then asked the crowd what they thought. The problems
were mostly due to dumb (but real world mistakes) - the kind of thing you'd slap
your fellow programmer for writing. These guys are definitely fun to listen to
- next up is Tiger and what's new in 1.5 (I thought it was 5.0?). Boy, it's a
full room tonight - I'd bet there's around 120-150 people here.
Taming the Tiger
Major theme of "JDK 5" is ease of development with features like
generics, for-each loop, autoboxing/unboxing, enums, varargs, static imports
and annotations. It's designed to make programs clearer, shorter and safer by
providing linguistic support for commong idioms. Sidenote: Joshua said that
Neal wrote the compiler - and they've basically made it more rigorous so it
writes the boilerplate code for you. New features do no sacrifice compatibility
or compromise the spirit of the language. Neal has been using these features
for a couple of years now and he says he's really enjoyed them.
Goal of this talk is to make it easy for us to understand JDK 5 so we can start
using it in our development. Let's look at the different features of 5.0.
Generics, For-Each and Autoboxing/unboxing
Generics allow you to specify the element type of collection. Rather than specifying
a List - you specify it's contents - i.e. String. It's basically stronger typing
with less typing which enforces the specification at compile time. For example,
the following code using the new for-each syntax to iterate through a list of
TimerTasks in a collection. Notice the lack of casting and easy-to-read loop
syntax.
void cancellAll(Collection<TimerTask> c) {
for (TimerTask task : c) {
task.cancel();
}
}
Bytecode is the same as it is in 1.4 - 5.0 merely converts the code for you.
One question that these guys have heard a lot is why ":" rather
than "in". The answer is twofold - because "in"
is already a keyword (for example, System.in) and they didn't want to introduce
a new keyword. Only new keyword in JDK 5 is enum.
The Collection Interface has been Generified. All existing code should
still work, but you can also use the new stuff if you like. I haven't listened
much to what's new in 5.0 - but this is wicked cool. You might say it sucks
because now you end up with strongly typed stuff, but at least you won't have
any more ClassCastExceptions.
- autoboxing: automatic conversion from int to Integer
- unboxing: automatic conversion from Integer to int
For example, you can now easily do the following:
Integer i = new Integer(5);
Map map = new HashMap();
map.put("result", i+1);
Notice that the Integer type is converted to an int for the addition, and then
back to an Integer when it gets put into the Map. Cool, huh?
JDK 5 also simplifies reflection. Class Class has been generified - Class literal
Foo.class is of type Class<Foo>. This enables compile-time type-safe reflection
w/o casting. The following used to return an Object and required casting.
Foo foo = Foo.class.newInstance();
This enables strongly typed static factories. I wonder if this can be used
with Spring so you don't have to cast a bean when grabbing it from the ApplicationContext?
When should you use Generics? Any time you can - unless you
need to run on a pre-5.0 VM. The extra effor in generifying code is worth it
- especially b/c of increased clarity and type safety.
When to use for-each loop? Any time you can b/c it really
beautifies code and makes it much easier to write. It's probably the smallest
new feature in 5.0, but likely to be a favorite. You can't use for-each for
these cases:
- Removing elements as you traverse a collection (b/c there's no iterator)
- Modifying the current slot in an array or list (b/c the index is hidden)
- Iterating over multiple collections or arrays
The lack of an index seems to rub the crowd wrong. Joshua and Neal's response
is they tried to design something very simple that would capture 80% of usage.
If you need an index, just use the old for loop - it ain't that hard; we've
been doing it for years!
If you want to use for-each in your APIs - i.e. if you're writing a framework,
a class should implement the new Iterable class.
When should you use autoboxing? When there is an impedance
mismatch b/w reference types and primitives. Not appropriate for scientific
computing. An Integer is not a substitute for an int. It simply hides the distinction
between wrappers and primitives. A null unboxes by returning a NullPointerException.
They did consider setting it to the primitive's default, but the community voted
50-1 to for NPE.
Enums
JDK 5 includes linguistic support for enumerated types. Advanced OO features
include the ability to add methods and fields to enums. Much clearer, safer,
more powerful than existing alternatives (i.e. int enums).
enum Season { WINTER, SPRING, SUMMER, FALL }
I just noticed that it's boiling in here - A/C must be out again in the
auditorium. It's 8:20 right now, I hope this is over soon, I can feel sweat
beading on my forehead.
Enums are Comparable and Serializable. Enum constants should be named similar
to constants. Enums are basically a new type of class. As far as I can tell,
I have no use for Enums in my code. There's lots of gasps from the crowd as
Joshua is describing the features of Enums (i.e. constant-specific methods).
Sure it looks cool, but I still don't think I have a use for it. Maybe framework
developers will find this useful. BTW, there's two high-performance collection
classes: EnumSet (bit-vector) and EnumMap (array). EnumSet replaces traditional
bit-flags: i.e. EnumSet.of(Style.BOLD, Style.ITALIC).
When should you use Enums?
- Natural enumerated types: days of week, phases of moon, seasons
- Other sets where you knkow all possible values: choices on menus, rounding
modes, command line flags
- As a replacement for flags (EnumSet)
Quote of the night: "It's extraordinarily rare that you'll need to cast
when programming with JDK 5".
Varargs
A method that takes an arbitrary number of values requires you to create an
array. Varargs automates and hides the process. James Gosling contributed the
... syntax. Varargs always has to be the last parameter. MessageFormat.format
has been retrofitted with varargs in JDK 5:
public static String format(String pattern, Object... arguments);
String result = MessageFormat.format("At {1,time} on {1,date}, there was {2} on planet "
+ "{0,number,integer}.", 7, new Date(),
"a disturbance in the Force");
Reflection is now much easier with Varargs - so you can call c.getMethod("test").invoke(c.newInstance())
instead of c.getMethod("test", new Object[0]).invoke(c.newInstance(),
new Object[0])).
When should you use Varargs?
- If you're designing your own APIs - use it sparingly.
- Only when the benefit is compelling. Don't overload a varargs method.
- In clients, when the API supports them: reflection, message formatting,
printf
Static Imports
Clients must qualify static members with class name (Math.PI). To avoid this,
some programmers put constants in an interface and implement it. BAD - "Constant
Interface Antipattern". They've made this mistake in the JDK - java.util.jar
has this pattern. Static import allows unqualified access to static member w/o
extending a type. All static fields, methods, etc. will be available for your
class using static imports. For example:
import static java.lang.Math.*;
r = cos(PI * theta);
When should you use Static Imports?
Very sparingly - overuse makes programs unreadable.
Only use it when tempted to abuse inheritence.
Metadata
Decorates programs with additional information. Annotations don't directory
affect program symantecs. They *can* affect treatment by tools and libraries.
Can be read from: source, class files, or reflectively. Ad hoc examples: transient,
@deprecated. Tiger provides a general purpose metadata facility.
Why Metadata?
- Many APIs require a fair amount of boilerplate - i.e. JAX-RPC.
- Many APIs require "side files" to be maintained. Examples: BeanInfo
class, deployment descriptor.
- Many APIs use naming patterns, i.e. JUnit.
Metadata encourages a declarative programming style - tell a computer what
to do, now how to do it. Annotation Type Declarations are similar to interface
declarations. Special kinds of annotations include Marker annotations and Single-element
annotations. The main reason for annotations is for tools providers.
Neal thought that JDK 5 Beta 3 or Release Candidate was available at http://java.sun.com/j2se/1.5.0,
but it looks like Beta 2 is the latest release. The fact that he said that implies
that a new release should be available shortly. Neal also mentioned that JDK
5 (final) would be shipping soon.
Random fact: Google uses a lot of Java - entire Ads front-end
is done in Java.
This was a great talk about all the new features of JDK 5 - I can't wait to
start using them. It might be awhile before I can convert AppFuse to JSP 2.0
and JDK 5, but it'll be a good day when I can write my apps using these technologies.
Tonight was the best overview of JDK 5 that I've seen so far - in print or person.
Update: Presentations PDFs have been published: Programming
Puzzles and Taming
the Tiger. August 12, 2004 01:01 AM MDT Permalink
About the author
Matt Raible
matt@raibledesigns.com
Blog: http://www.raibledesigns.com/page/rd
Matt currently resides in Denver where he consults as a J2EE Developer for Raible Designs and is always striving to find the easiest solutions for web applications. His current favorite technologies can be found within his open source AppFuse application. He is actively involved in the Open Source community and loves Java.
|