JDK 5 in Practice

Java Development News:

JDK 5 in Practice

By Cedric Beust

05 Nov 2004 | TheServerSide.com

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.