Writing Your Last For-Loop - Beautiful Code:
I was sitting with some friends the other week and a question came up. Someone asked, ???so, when did you last write a for-loop????
I remember reading this post the other day nodding to myself in agreement about how using closures and Java's new enhanced for loop makes the code a lot cleaner and easier to write, and then this evening a hit a bug in the TestNG IDEA plugin triggered by using this exact "improvement".
The problem with these closure/iterator-less loops in that when you use them you loose state information about the progress of your loop. The problem I hit with the plugin was with adding customer listeners to a test profile - the command line to launch TestNG was being generated as such:
-listener com.theoryinpractise.testng.requirements.RequirementsTransformer;
The problem? The trailing ; delimiter. When TestNG splits the list of listeners two items are returned: "com.theoryinpractise.testng.requirements.RequirementsTransformer" and "" of which the empty string triggers a ClassNotFoundException (one could argue this is a bug in three places - the split returning an empty string, TestNG not even attempting to load a class named "", and me for appending too many delimiters - we're all at fault imho).
But I digress, the code which triggered the problem:
if (data.TEST_LISTENERS != null && !data.TEST_LISTENERS.isEmpty()) {
StringBuilder sb = new StringBuilder();
for (String listenerClassName : data.TEST_LISTENERS) {
if (listenerClassName != null && !"".equals(listenerClassName)) {
sb.append(listenerClassName).append(";");
}
}
javaParameters.getProgramParametersList().add(
TestNGCommandLineArgs.LISTENER_COMMAND_OPT, sb.toString());
}The code is simply appending ";" after the listener class name on each loop without considering we don't want a trailing separate.
The new enhanced for construct in Java 5 hides from the code any means to ask "is there anything to do after this iteration?" or even "what iteration am I on?". Common solutions to this would be to add code to trim the trailing ";", or increment a count variable on each iteration, all ways to reclaim some lost functionality.
So it seems there's still life in the old for loop yet.
Technorati Tags: development, java, testing
It's better practice, IMO, to just create an array of strings when going
through the loop. (I.e. just do "sb.append(listenerClassName);", without
appending the ";".) Then you simply join all the strings up with your
chosen delimiter. Less chance of bugs that way, expressly _because_ you
don't have to treat the first and last items differently. (It all boils
down to lisp in the end.) Something like the following Perl pseudo-code:
I use iterators in that case. It's easy just to ask iterator.hasNext() and
only add the ";" if true.
Well, the "enhanced" for loop for iterating through Collections is not
supposed to be a replacement for the already existing loop, it's just an
add-on.
The Ruby-ish way of doing this is: