我想在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;
}
但这不起作用,因为编译器不希望泛型类型变量进一步专门化(我认为)。
关于如何使这项工作的任何想法?
答案 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);
}