Java Generics…

On 14 April 2009, in Java, Nuisances, Programming, by stacy

java_logo_21Grrr.  I really wish Java had either:

  • Reified generics or
  • Closures

In the interim, I offer the following Java code to the world.  Does it help?  Use at your own risk.

This is a class that wraps the collections interface to provide for recovery of the type parameter.  To use it, make a new instance and pass the class object for the parameter to the constructor, along with the original collection.

package gypsum.util;
 
import java.util.Collection;
import java.util.Iterator;
 
/**
 * This class packages some implementation of {@link Collection} along with
 * the class object for the type parameter of the collection.  This allows
 * getting the type of objects in the collection using the {@link #getType()}
 * method, and then using the resulting class to perform casts.  This basically
 * creates a "reified" generic collection (of sorts).
 *
 * @param    Class of data held in this collection.
 * @author sprowell
 * @version $Revision$ $Date$
 */
public class TypedCollection implements Collection {
 
    private Class type;
    private Collection implementation;
 
    /**
     * Make a new instance.
     * @param type              The class object for the type of elements.
     * @param implementation    The implementation.
     */
    public TypedCollection(Class type, Collection implementation) {
        if (type == null) {
            throw new NullPointerException("The type is null.");
        }
        if (implementation == null) {
            throw new NullPointerException("The implementation is null.");
        }
        this.type = type;
        this.implementation = implementation;
    }
 
    /**
     * Get the type held in this collection.
     * @return  The type held in this collection.
     */
    public Class getType() {
        return type;
    }
 
    /**
     * Recover the original typed collection.  In order for this to work,
     * the correct class must be supplied; otherwise you will get a
     * {@link ClassCastException}.  Use this as follows.
     *
<pre>     * TypedCollection&lt;Foo&gt; coll = oldcoll.recover(oldcoll.getType());
     *
     *
 
     * This converts from {@code TypedCollection&lt;?&gt;} to
     * {@code TypedCollection&amp;ltT&gt;}, so you recover the generic type
     * parameter.
     *
 
     * This may seem backward, or even a little contrived.  Blame it on
     * the Java generics system.  The reason you must pass the class
     * object <em>in</em>, even though it is maintained here, is that
     * the compiler must know the type of the object <em>at the point
     * you use this method</em>.  Thus you can do the following.
     *
<pre>     * public void print(TypedCollection&lt;?&gt; coll) {
     *   if (coll.getType() == String.class) {
     *     TypedCollection&lt;String&gt; coll = coll.recover(String.class);
     *     for (String str : coll) {
     *       System.out.println(str);
     *     }
     *   }
     * }
     * &lt;/pre&gt;
     *
     * @param    The type of the desired collection.
     * @param kind  The class object for type T.
     * @return  The correctly cast generic collection.
     * @throws  ClassCastException
     *          This instance is <em>not</em> the type desired.
     */
    @SuppressWarnings("unchecked")
    public  TypedCollection recover(Class kind) {
        if (kind == type) {
            return (TypedCollection) this;
        }
        throw new ClassCastException("Incorrect cast.");
    }
 
    /**
     * {@inheritDoc}
     * @see java.util.Collection#add(java.lang.Object)
     */
    @Override
    public boolean add(E arg) {
        return implementation.add(arg);
    }
 
    /**
     * {@inheritDoc}
     * @see java.util.Collection#addAll(java.util.Collection)
     */
    @Override
    public boolean addAll(Collection arg) {
        return implementation.addAll(arg);
    }
 
    /**
     * {@inheritDoc}
     * @see java.util.Collection#clear()
     */
    @Override
    public void clear() {
        implementation.clear();
    }
 
    /**
     * {@inheritDoc}
     * @see java.util.Collection#contains(java.lang.Object)
     */
    @Override
    public boolean contains(Object arg) {
        return implementation.contains(arg);
    }
 
    /**
     * {@inheritDoc}
     * @see java.util.Collection#containsAll(java.util.Collection)
     */
    @Override
    public boolean containsAll(Collection c) {
        return implementation.containsAll(c);
    }
 
    /**
     * {@inheritDoc}
     * @see java.util.Collection#isEmpty()
     */
    @Override
    public boolean isEmpty() {
        return implementation.isEmpty();
    }
 
    /**
     * {@inheritDoc}
     * @see java.util.Collection#iterator()
     */
    @Override
    public Iterator iterator() {
        return implementation.iterator();
    }
 
    /**
     * {@inheritDoc}
     * @see java.util.Collection#remove(java.lang.Object)
     */
    @Override
    public boolean remove(Object o) {
        return implementation.remove(o);
    }
 
    /**
     * {@inheritDoc}
     * @see java.util.Collection#removeAll(java.util.Collection)
     */
    @Override
    public boolean removeAll(Collection c) {
        return implementation.removeAll(c);
    }
 
    /**
     * {@inheritDoc}
     * @see java.util.Collection#retainAll(java.util.Collection)
     */
    @Override
    public boolean retainAll(Collection c) {
        return implementation.retainAll(c);
    }
 
    /**
     * {@inheritDoc}
     * @see java.util.Collection#size()
     */
    @Override
    public int size() {
        return implementation.size();
    }
 
    /**
     * {@inheritDoc}
     * @see java.util.Collection#toArray()
     */
    @Override
    public Object[] toArray() {
        return implementation.toArray();
    }
 
    /**
     * {@inheritDoc}
     * @see java.util.Collection#toArray(T[])
     */
    @Override
    public  T[] toArray(T[] a) {
        return implementation.toArray(a);
    }
}
Share
Tagged with:  

3 Responses to Java Generics…

  1. Neal Gafter says:

    See java.util.Collections.checkedCollection. It’s been there since Java 5.

  2. stacy says:

    The checkedCollection() doesn’t seem to give me what I want; specifically, the recover(Class) method. That is, I have a method that must take a Collection<?>, but I really want a Collection<Foo> or Collection<Bar>. I can check the type and then use recover(Foo.class) or recover(Bar.class).

    Collection<Foo> fooColl = coll.recover(Foo.class);

    I’m then free to pass this on to methods that require the correct type.

  3. dolcraith says:

    Doesn’t work for nested generics, but nothing really does. I believe this is one of Java’s weakest points.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" highlight="">