A trial port to JDK1.5

2 AM January 14, 2005

I did a trial port of our application from JDK1.4 to JDK1.5, using Eclipse 3.1M4. The results were encouraging, but we won't be moving across right away.

To give you some idea of the size of our project, it is comprised of two sub-projects: the first is a bunch of utility and helper classes that runs to about 10k lines, and the other is the main application at about 40k lines. We make good use of open source libraries and frameworks, including Spring, Hibernate, Apache Axis and Jakarta Commons.

Doing a port

I first recompiled the utility project under JDK 1.5, coming up with 28 warnings and no errors. A fair number of the warnings were to do with not having a serialVersionUID on a serializable class. I gave up fixing those when it wanted me to put a serialVersionUID in an anonymous subclass of javax.swing.AbstractAction. (As an aside, Eclipse lets you turn off a warning completely, but it would be nice if I could have turned off this warning for subclasses of Swing classes.)

Most of the rest of the warnings were fixed by simply adding type parameters to collection types; for example, List might become List<String>. I also removed unneeded casts, and used the new for-loop syntax wherever appropriate. This is a fairly routine, mechanical process, and I was done in about two hours.

I then loaded up the bigger project and promptly received 31 errors and 400 or so warnings.

The errors were because we use the commons-lang Enum class, and enum is a reserved word in JDK 1.5. The fix was to convert to the new Java enumeration types. This was straight forward, mostly because the JDK1.5 and commons-lang enums are both based on Joshua Bloch's typesafe enum idiom.

Converting from bare types to parameterised types was interesting - it forced me to determine the type of every collection, and it wasn't always obvious. By putting the type of the contained element with the collection declaration, it is easier for somebody casually browsing the source code base to understand it.

One very cute method buried in the JDK1.5 library is Arrays.asList(), which converts an arbitary bunch of parameters into a list. For example, we have this ugly code to build a list of three Strings and pass them to a constructor:


DispatchSourceTextGroup dstg =

    new DispatchSourceTextGroup(new ArrayList() {

    {

        add("FIRE INDICATOR PANEL");

        add("CFA-T3106130011-1");

        add("FULL CALL");

    }

});

Naively converting to generic types yields this typesafe, but even uglier code:


DispatchSourceTextGroup dstg =

    new DispatchSourceTextGroup(new ArrayList<String>() {

    {

        add("FIRE INDICATOR PANEL");

        add("CFA-T3106130011-1");

        add("FULL CALL");

    }

});

Arrays.asList() lets us write this comparatively nice code instead:


DispatchSourceTextGroup dstg = new DispatchSourceTextGroup(

    Arrays.asList(

            "FIRE INDICATOR PANEL", 

            "CFA-T3106130011-1",

            "FULL CALL"));

My only gripe is that it is not obvious (from this usage) why the method is on the Arrays utility class. Perhaps it could be aliased to Collections.asList() in a future release?

The new for loop syntax gave us some good wins too. For example this nested loop:


public void checkExtrasGuids() {

    for (Iterator i1 = extrasMap.keySet().iterator(); i1.hasNext();) {

        String kioskName = (String) i1.next();

        for (Iterator i2 = ((List) extrasMap.get(kioskName)).iterator(); i2

                .hasNext();) {

            DispatchExtras extras = (DispatchExtras) i2.next();

            checkKioskHasGuid(kioskName, extras.getDispatchGuid());

        }

    }

}

becomes more understandable:


public void checkExtrasGuids() {

    for (String kioskName : extrasMap.keySet()) {

        for (DispatchExtras extras : extrasMap.get(kioskName)) {

            checkKioskHasGuid(kioskName, extras.getDispatchGuid());

        }

    }

}                    

I didn't finish the port of this part of our application, but based on my progress over three hours, it would take two days to complete. My main area of concern is that we may need to move from Hibernate 2.1 to 3.0, which is being developed with JDK1.5 in mind.

Conclusion

JDK 1.5 gives our project some small but definite wins. They would be big enough to justify a switch from JDK 1.4, except for one thing: IBM has released Eclipse 3.0-based Rational tools which have some really neat features for visualising the structure of your application. Based on the trial version that I (finally, with much effort) downloaded, modelling tools will be more useful than language features, so JDK 1.5 can wait until IBM's tools catch up.

By alang | # | Comments (6)
(Posted to javablogs and Java)

Comments

At 05:00, 14 Jan 2005 Yuri Schimke wrote:

Arrays.asList(Object[]) is there in JDK1.4 as well. It takes an Object[] as arguments, but in JDK1.5 that means it also accepts variable length arguments.

(#)
At 05:40, 14 Jan 2005 Alan Green wrote:

Yes, the variable args syntax really makes all the difference.

(#)
At 07:33, 14 Jan 2005 rentzsch wrote:

The old DispatchSourceTextGroup code and the newer one "naively converted to generic types" are identical?

(#)
At 18:06, 14 Jan 2005 Keith Lea wrote:

IntelliJ IDEA provides an automatic conversion process, where it converts all Iterator-based for loops to new foreach loops (only where it wouldn't change the semantics of the code), and removes manual boxing (converting to autoboxing), and converts all uses of raw versions of generic classes (like new ArrayList()) to generic versions, based on the types of objects you place in there.

Maybe this would make your conversion process easier.

(#)
At 19:47, 14 Jan 2005 Alan Green wrote:

Thanks, Rentzsch - I fixed it. (The angle brackets were causing the <String> bit to be interpreted as some kind of tag by the browser. That's what I get for naively converting to HTML.)

(#)
At 00:34, 16 Jan 2005 Richard wrote:

Yeah, Arrays.asList has been lurking in the JDK since the beginning (if the javadoc is trustworthy). I know I've been using it since 1.3.

Another couple of things you should watch for:

- use StringBuilder rather than StringBuffer. Every method in StringBuffer is synchronized, meaning that you're going to be paying for acquiring and releasing locks unnecessarily. Note that the built-in concatenation has switched to doing this, so code like String x = "a" + "b"; will run faster than String x = new StringBuffer("a").append("b").toString();

- remove any calls to new Integer, Boolean, etc. There are now (finally!) valueOf() static methods for each of these, if you still need to explicitly create a boxed type, or just let the autoboxing work its magic.

- watch out for oddities introduced by autoboxing and the new for loop syntax. Guess what the following code prints:

  List<Integer> vals = Arrays.asList(1,2,3); 
  for (Integer val: vals) {
    val += 10;
  }
  System.out.println(vals);

Answer: [1,2,3]

Note that this code will compile without any errors or warnings, so the attempted increment will just get thrown away. Use final in the for loop to make this a compile-time error.

  List<Integer> vals = Arrays.asList(1,2,3);
  for (final Integer val: vals) {
    val += 10; // compiler error here
  }
  System.out.println(vals);

This might be obvious to those of us who've always used iterators explicitly, but not all of us started using Java in '96 either...
Of course, if you still want to do this increment, try:

  List<Integer> vals = Arrays.asList(1,2,3);
  for (Iterator<Integer> it = vals.iterator; it.hasNext();) {
    Integer val = it.next();
    val += 10;
    it.set(val);
  }
  System.out.println(vals);

Eww, explicit Iterators are really ugly now.

- the new varargs syntax can make some overloads (eg. append(Object) and append(Object, Object)) unreachable code [or at least make it very difficult for a user to figure out which is called] - Java will simply convert these Objects to Object[]s and call append(Object[]) (or append(Object...) if you've updated the syntax). If you control all the calling code too, seek out and remove these overloads.

- beware of generic arrays. Not using arrays in your code anymore? [hint: use a List instead] Good, then you've got a chance of avoiding this issue. But note that varargs are just an array, and it's easy to get in the habit of using T... where you used to have Object[]. But note that a) you can't create a generic array, b) the compiler cannot ensure that array members are of type T, and c) this can be a real PITA. This is all due to variance-related issues and Java 5's use of erasure, and can make implementing the toArray() methods of List (and anything similar, like a generic version of the CompositeCollection.CollectionMutator from Jakarta Commons Collections) quite difficult.

There's another bunch of pitfalls to trap the unwary, but I'll leave it for others to document...

(#)

Add Comment




(Not displayed)






(Leave blank line between paragraphs. URLs converted to links. HTML stripped. Indented source code will be formatted with <pre> tags.)




© 2003-2006 Alan Green