November 5, 2004
I have been writing JDK 5.0 code for over six months now, so I thought I would
take some time to reflect on my experience and draw a few conclusions on the features
that were introduced.
Enhanced for loop
The undisputed winner. I can't even begin to describe how good it feels to
use the new for loop everywhere (well, almost everywhere). I mentally cringe
the few times when I am forced to use the old for loop, typically when I need
the index or that I want the Iterator to be visible outside the loop.
The code is much more readable and feels less cluttered with noise (e.g. indices
when you don't need them or incrementation exposing the underlying implementation).
This latter point was an unexpected benefit of the new loop, by the way. Imagine
that you have:
String[] names = ...;
for (String name : names) {
// ...
}
and you decide that you want to change the type of names to a Collection. How
do you modify your code?
List<String> names = ...;
for (String name : names) {
// ...
}
That's right, just one line. It doesn't get better than that.
Annotations
Obviously, I am partial to annotations since they are at the heart of TestNG
but I am a firm believer that annotations are going to change the way we build
software in Java. We have been relying for far too long on reflection hacks
to introduce meta-data in our programs, and annotations are finally going to
provide an excellent solution to this problem.
Also, I haven't felt the need to use some of the predefined annotations such
as @Override, so I haven't formed an opinion on them yet.
It seems inescapable to me that in a couple of years, most of the Java code
that we will be reading and writing will contain annotations.
Static imports
I hardly use them at all, except in my annotations type for Retention and Target.
I am still not convinced that the original intent that motivated the addition
of this feature (discourage the anti-pattern of implementing an interface to
be able to reuse its constants without having to qualify them) justifies the
introduction of a new language feature, but time will tell.
I guess that in some way, the use of an IDE in my day-to-day programming makes
imports absolutely obsolete, so I can't really get myself to feel strongly about
this feature anyway.
Variable-length arguments
I haven't had the need for this feature at all so far. It might come in handy
once in a while but I'm really not convinced it warranted a change in the language.
Enums
While I definitely give Enums a theoretical nod of approval, I haven't really
converted my code to them yet, and I haven't acquired the reflex to use them
either. I believe that when I do, I'll be happy with the result and it will
make my code a tad more robust.
Generics
I left the best for the end... but since this entry is getting a bit long,
I will save the Generics discussion for tomorrow.
Autoboxing
I have stayed away from autoboxing so far, probably because I have a vague
feeling of losing control of the performance of my code. I don't think it is
justified, though, so autoboxing can come in handy and make your code a little
bit more readable. I think I would encourage developers to flag their code when
such autoboxing is happening, and I am pretty sure that IDE's will soon be able
to do the same.
Generics
Where to start?
Well, first of all, nobody can dispute that Generics are a solid concept that
tends to improve the robustness of your code. The reason why they are so usually
controversial regardless of the language is because of their implementation.
And for having been a member of the C++ committee for many years , I can definitely
vouch for the difficulty of getting them right.
In a nutshell, I have this to say about Java generics: my code feels more robust,
but it's harder to read.
So what's the problem?
Redundancy.
First of all, I have always had a hard time with the redundancy introduced
by the necessity of casting in general. For example, instead of writing:
Map accounts = new HashMap(); // no generics
...
Account a = (Account) accounts.get("Cedric");
why can't I just write:
Map m = new HashMap(); // no generics
...
Account a = m.get("Cedric");
and let the compiler introduce a silent cast, since obviously, it's an object
of type Account that I am trying to retrieve from the Map?
Obviously, Generics don't solve this problem entirely but they make a decent
job at alleviating it somewhat. But they also make it worse in some other ways:
Map<String, List<Account>> accounts =
new HashMap<String, List<Account>>();
Ouch.
Not only is the code significantly harder to read, but it fails to obey the
DRY principle ("Don't repeat yourself"). What if I need to change
the value type of this map from List<Account> Collection<Account>?
I need to replace all these statements everywhere in my code. While IDE refactoring
will help, it's still an awful lot of code for a modification of this kind that
hardly impacts the semantics of this code.
Admittedly, there is no nice way to avoid this syntax when you are creating
a new object, but what I am driving at is that I think Generics would have been
better off if typedefs had been introduced with them.
Or so I thought at first.
But after thinking about it more, I realized that typedefs were the wrong solution
to the problem, because simply put, they don't add anything to the use of a
separate class to define your complex Generic type.
class AccountMap extends HashMap<String, List&lAccount>> {
...
}
Except for the fact that you need to extend an implementation (HashMap, and
not Map, obviously), this solution is probably better than introducing typedef,
which has its own quirks.
I haven't gone to this trouble so far, but my recommendation would be: do it
if you write the type more than three times (twice in the initialization and
you use it more than once in your code).
Except for this little annoyance, I am quite happy with Generics overall and
I particulary enjoy reading the TestNG
Javadocs so nicely typed.
Conclusion
I am very happy with the new features of JDK 5.0 and I am quite proud to have
had a chance to influence it with my participation in JSR 175 and JSR 201. Like
all radical evolutions, not all of the new features will be popular with everyone,
but as long as most developers find some of these features useful and that backward
compatibility is preserved, I think JDK 5.0 is a very solid step toward more
solid Java code.
About the author
Cedric Beust
cedric@beust.com
Blog: http://www.beust.com/weblog/
Cedric Beust is a Senior Software Developer on the WebLogic Server team and provides his thoughts on J2EE, Java, AOP and software development in his weblog, Otaku.
|