混合泛型类型变量以在Java中实现类型安全的映射函数

时间:2010-07-28 12:45:42

标签: java generics type-safety

我想在Java中编写一个类型安全的map方法,它返回与传递的参数相同类型的Collection(即ArrayList,LinkedList,TreeSet等),但具有不同的泛型类型(在有角度的括号之间),由另一个参数的泛型类型(通用映射函数的结果类型)确定。

因此代码将用作:

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);

这个想法应该是这样的:

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;
}

但这不起作用,因为编译器不希望泛型类型变量进一步专门化(我认为)。

关于如何使这项工作的任何想法?

4 个答案:

答案 0 :(得分:2)

AFAIK在Java中无法做到这一点,因此它既是(a)编译类型安全的,又是(b)map的调用者不需要重复源和目标的集合类型。 Java不支持higher-order kinds,您要求的内容可以在Scala 2.8中实现,但即使实现细节有点复杂,请参阅this SO question

答案 1 :(得分:1)

似乎无法在泛型类型上使用泛型类型。由于您只需要有限数量的那些,您可以枚举它们:

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

我使用CS作为源集合,使用CR作为结果集合。我担心你无法删除演员,因为你不能在运行时使用泛型。 newInstance()只是创建一个类型的对象集合的对象,并且需要转换为CR来满足编译器。但它仍然是一种欺骗。这就是编译器发出警告的原因,你必须用@SuppressWarnings("unchecked")来压制。

有趣的问题btw。

答案 2 :(得分:0)

你想做什么是不可能的。但是,您可以指定返回集合的泛型类型。

public static <S, R> Collection<R> map(Collection<S> collection, TransformFunctor<S,R> f) throws Exception {
    Collection<R> result = collection.getClass().newInstance();
    for (S i : collection) {
        result.add(f.apply(i));
    }
    return result;
}

这会返回您使用参数指定的类型的集合,它不会显式地这样做。但是,将方法返回转换为参数的集合类型是安全的。 但是,您应该注意,如果您传递的集合类型没有no-arg构造函数,代码将失败。

然后您可以像这样使用它:

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

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

答案 3 :(得分:0)

这有效:

class Test {

    public static void main(String[] args) throws Exception {
        Function<Integer, String> i2s = new Function<Integer, String>() {
            public 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);
        System.out.println(""+ali+als+tss);
    }

    static <SC extends Collection<S>, S, T, TC extends Collection<T>> TC map(
            SC collection, Function<S, T> func) throws Exception {
        // if this casting can be removed, the better
        TC result = (TC) collection.getClass().newInstance();
        for (S i : collection) {
            result.add(func.apply(i));
        }
        return result;
    }

}

interface Function<S, R> {
    R apply(S src);
}
相关问题