Search code examples
javagenericstype-safety

mix generic type variables to implement a type-safe map function in Java


I want to write a type-safe map method in Java that returns a Collection of the same type as the argument passed (i.e. ArrayList, LinkedList, TreeSet, etc.) but with a different generic type (that between the angled brackets), determined by the generic type of another parameter (the resulting type of the generic mapping function).

So the code would be used as:

public interface TransformFunctor<S, R> {
    public R apply(S sourceObject);
}

TransformFunctor i2s = new TransformFunctor<Integer, String>() {
    String apply(Integer i) {return i.toString();};

ArrayList<Integer> ali = new ArrayList<Integer>(Arrays.asList(1, 2, 3, 4));
ArrayList<String> als = map(ali, i2s);

TreeSet<Integer> tsi = new TreeSet<Integer>(Arrays.asList(1, 2, 3, 4));
TreeSet<String> tss = map(tsi, i2s);

The idea would be something like:

public static <C extends Collection<?>, S, R>
C<R> map(final C<S> collection, final TransformFunctor<S, R> f)
throws Exception {
    //if this casting can be removed, the better
    C<R> result = (C<R>) collection.getClass().newInstance();
    for (S i : collection) {
        result.add(f.apply(i));
    }
    return result;
}

but that doesn't work because the compiler isn't expecting generic type variables to be further specialised (I think).

Any idea on how to make this work?


Solution

  • AFAIK there is no way to do this in Java so that it's both (a) compile-type-safe and (b) the caller of map does not need to repeat the collection type of source and target. Java does not support higher-order kinds, what you're asking for can be achieved in Scala 2.8, but even there the implementation details are somewhat complex, see this SO question.