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

混合泛型类型变量以在Java中实现类型安全映射函数,java,generics,type-safety,Java,Generics,Type Safety,我想用Java编写一个类型安全的map方法,该方法返回与传递的参数类型相同的集合(即ArrayList、LinkedList、TreeSet等),但具有不同的泛型类型(尖括号之间的泛型类型),由另一个参数的泛型类型(泛型映射函数的结果类型)确定 因此,代码将用作: public interface TransformFunctor<S, R> { public R apply(S sourceObject); } TransformFunctor i2s = new Tra

我想用Java编写一个类型安全的
map
方法,该方法返回与传递的参数类型相同的集合(即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);
公共接口转换函数{
公共R应用程序(S sourceObject);
}
TransformFunctor i2s=新TransformFunctor(){
字符串apply(整数i){return i.toString();};
ArrayList ali=新的ArrayList(Arrays.asList(1,2,3,4));
ArrayList als=map(ali,i2s);
TreeSet tsi=新树集(Arrays.asList(1,2,3,4));
树集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;
}

public static似乎不可能在泛型类型上使用泛型类型。因为您只需要有限数量的泛型类型,所以可以枚举它们:

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;
}
公共静态CR映射(
最终CS集合,最终变换子f)
抛出异常{
//如果这个铸件可以去掉,就更好了
CR结果=(CR)collection.getClass().newInstance();
适用于(S i:集合){
结果。添加(f.应用(i));
}
返回结果;
}
我将
CS
用作源集合,将
CR
用作结果集合。恐怕您无法删除强制转换,因为您无法在运行时使用泛型。
newInstance()
只创建一个类型为某个对象集合的对象,强制转换为
CR
是满足编译器要求所必需的。但这仍然是一种欺骗。这就是编译器发出警告的原因,您必须使用
@SuppressWarnings(“未选中”)
抑制该警告


有趣的问题。顺便说一句。

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

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;
}
公共静态集合映射(集合集合,TransformFunctor f)引发异常{
收集结果=Collection.getClass().newInstance();
适用于(S i:集合){
结果。添加(f.应用(i));
}
返回结果;
}
这会返回一个与参数指定的类型相同的集合,但不会显式返回。但是,将方法return强制转换为参数的集合类型是安全的。 但是,您应该注意,如果您传递的集合类型没有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);
arraylistali=newarraylist(Arrays.asList(1,2,3,4));
ArrayList als=(ArrayList)地图(ali,i2s);
TreeSet tsi=新树集(Arrays.asList(1,2,3,4));
树集tss=(树集)映射(tsi,i2s);
这项工作:

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);
}
类测试{
公共静态void main(字符串[]args)引发异常{
函数i2s=新函数(){
公共字符串应用(整数i){
返回i.toString();
}
};
ArrayList ali=新的ArrayList(Arrays.asList)(1,2,3,
4));
ArrayList als=map(ali,i2s);
TreeSet tsi=新树集(Arrays.asList(1,2,3,4));
树集tss=map(tsi,i2s);
系统输出打印项次(“+ali+als+tss”);
}
静态TC图(
SC集合,函数func)引发异常{
//如果这个铸件可以去掉,就更好了
TC result=(TC)collection.getClass().newInstance();
适用于(S i:集合){
结果.添加(函数应用(i));
}
返回结果;
}
}
接口函数{
R适用(S src);
}

AFAIK在Java中无法做到这一点,因此(a)编译类型安全(b)
map
的调用方不需要重复源和目标的集合类型。Java不支持,您所要求的可以在Scala 2.8中实现,但即使在Scala 2.8中,实现细节也有点复杂,请参见。

它可以工作,但并不强制返回集合的类型等于so的类型Source collection.Mine也没有,但至少它有较少无用的泛型类型;)它不安全。你可以做
ArrayList tss=map(tsi,i2s);
它将编译相同的,但在运行时失败…不需要您的CS和CR泛型类型。请参阅我的答案。这取决于。如果删除它们,该方法将失去一些表达能力。您的解决方案需要对结果进行强制转换(如果您想要比
集合更具体的内容
).Mine不需要对所有与输入类型或超级类型相同的类型进行强制转换。当然,许多类型变量都很难看。但它使使用更容易,而且我认为用户的生活应该总是比实现者更简单。如果
集合
允许的
添加操作不受支持,它也会失败e> 接口或构造函数是非公共的。