luni, 3 martie 2008

Java 5 for and Enumerations: don't stop, add type tokens

Some time ago I bumped into this blog post that presented an Adapter from Enumeration to Iterable/Iterator. It was so simple and yet incredibly useful. How could I have not though of this? Imagine my 'dooh' moment, especially since I was working for some time now with an API that's exclusively based on Enumerations (I guess they are keen on extreme backwards compatibility - no iterators, not one!) and I was using JDK 1.5...

However, I think we can push things a little bit further (well, we can always do this, can't we?). Follow me on this one.

One of the main applications of the Adapter design pattern is to support legacy code or code that we can't change (like the one in vendor/third-party libs) but we need/would like it to conform to a certain interface (Iterable in our case).

Now, if we think a bit, most (if not all) of the legacy code based on Enumerations is not likely to support Generics either so simply using iterate() over raw Enumerations would end up in a bunch of unchecked warnings. We could leave it this way or we could add @SupressWarnings("unchecked") (which is not really a satisfying solution, is it?).

What we could do to improve things a little is to add an extra method (see the code below), similar to iterate(), that takes an additional type token as the second argument representing the expected type of the elements of the adapted Enumeration (one could read more about type tokens in Gilad Bracha's really nice Generics tutorial). I've also taken the liberty to rename the methods to in() to come closer to another nice topic - fluent interfaces that is.

01 package ro.thon.util;
02
03 import static ro.thon.util.Assert.notNull;
04
05 import java.util.Enumeration;
06 import java.util.Iterator;
07
08 public final class Iterators {
09
10 private Iterators() { /* prevent any instantiation... */ }
11
12 private static final class IterableEnumeration<E> implements Iterable<E>, Iterator<E> {
14
15 private Enumeration<?> enumeration;
16
17 private Class<? extends E> klass;
18
19 public IterableEnumeration(Enumeration<?> enumeration, Class<?
20 extends E> klass) {
21 notNull(enumeration, "non-null enumeration expected");
22 notNull(klass, "non-null type token expected");
23 this.enumeration = enumeration;
24 this.klass = klass;
25 }
26
27 public IterableEnumeration(Enumeration<? extends E> enumeration) {
28 notNull(enumeration, "non-null enumeration expected");
29 this.enumeration = enumeration;
30 }
31
32 public Iterator<E> iterator() { return this; }
33
34 public boolean hasNext() { return enumeration.hasMoreElements(); }
35
36 @SuppressWarnings("unchecked")
37 public E next() {
38 Object next = enumeration.nextElement();
39 return (klass == null) ? (E) next : klass.cast(next);
40 }
41
42 public void remove() { throw new UnsupportedOperationException(); }
43 }
44
45 public static <E> Iterable<E> in(Enumeration<?> enumeration,
46 Class<? extends E> klass) {
47 return new IterableEnumeration<E>(enumeration, klass);
48 }
49
50 public static <E> Iterable<E> in(Enumeration<? extends E> enumeration) {
51 return new IterableEnumeration<E>(enumeration);
52 }
53 }

Now, calling the second version of in() eliminates those (really) annoying unchecked warnings.

Note that this is not entirely type safe, since we could easily pass a raw Enumeration of another type or (relying too much on code completion) we could pass another class token (with a similar name) but at least we got rid of the pesky warnings.

Anyway, great blog Stephan, thanks again. Me friend! :)