我正在努力确定使用泛型的正确方法签名,以便将类型A的Collection
转换为类型B的指定集合。例如,我有一个{{1 A,我希望该方法返回B的ArrayList
。到目前为止,我已经提出了
LinkedList
但是,如果我写下面的代码:
<A, B, C extends Collection<B>> C mapCollection(
final Iterable<A> source,
final Class<B> destinationClass,
final Class<C> collectionClass)
编译器警告ArrayList<TypeA> listOfA = getList();
LinkedList<TypeB> listOfB = mapCollection(listOfA, TypeB.class, LinkedList.class);
返回mapCollection
。显然,它使用LinkedList
参数来确定LinkedList.class
的类型,而不是看到C
扩展C
?
如果我这样做会变得更有趣
Collection<B>
因为那时,编译器会给出类型不匹配错误List<TypeB> = mapCollection(listOfA, TypeB.class, LinkedList.class);
。有趣的是,这只发生在Eclipse中,cannot convert from ArrayList to List<T>
对此代码非常满意。当然,我必须使用javac
我宁愿避免。
有关如何解决此问题的任何想法?或者这不可能吗?
答案 0 :(得分:2)
正如其他人所说,你正在遇到由擦除引起的Java泛型的限制。具体来说,类文字LinkedList.class
的类型为Class<LinkedList>
。它只能代表原始类型,如LinkedList
;它不能代表参数化类型,例如LinkedList<TypeB>
。
如果你有一个Class<LinkedList<TypeB>>
的实例,那么它可以工作:
Class<LinkedList<TypeB>> clazz = ... ;
List<TypeB> listOfB = mapCollection(listOfA, TypeB.class, clazz);
不幸的是,获得这样的类实例很笨拙。你可以通过raw来获取它:
@SuppressWarnings("unchecked")
Class<LinkedList<TypeB>> clazz = (Class<LinkedList<TypeB>>) (Class) LinkedList.class;
但这并不比现在的好。
您可能会调查Guava的TypeToken
类等技术(仍然是Guava 20的测试版)。有关背景信息,请参阅Neal Gafter的原始文章"Super Type Tokens"及其limitations的更新。
使用类型令牌的主要问题是它依赖于被调用者(在这种情况下为mapCollection
)能够创建目标类的实例的能力。按照惯例,Java的集合有一个无参数构造函数,所以这很简单,可以反射调用。但是,如果需要一些自定义,例如指定初始容量或用于排序的比较器,该怎么办?使用类型令牌时处理这些非常困难。
Java 8方法是传递一个函数 - Supplier
- 来创建目标实例。这允许调用者指定类型,以及任何专门的构造函数参数,同时让被调用者确定何时创建。为此,您将重写方法签名,如下所示:
<A, B, C extends Collection<B>> C mapCollection(
final Iterable<A> source,
final Supplier<C> supplier) { ... }
你用lambda表达式调用它:
List<TypeB> listOfB = mapCollection(listOfA, () -> new LinkedList<TypeB>());
或使用方法参考:
List<TypeB> listOfB = mapCollection(listOfA, LinkedList::new);
请注意,LinkedList
的类型参数在方法参考案例中是正确推断的。
作为奖励,mapCollection
实施不需要处理任何反映。它需要做的就是致电supplier.get()
。
P.S。请勿使用LinkedList
。
答案 1 :(得分:0)
类型删除正在妨碍你。看看这个question。在将C
类型参数定义为extends Collection<B>
时,您无法保留Class
有界类型参数(C
)。
您可以直接提供<A, B, C extends Collection<B>> void mapCollection(
final Iterable<A> source,
final Class<B> destinationClass,
final C collectionClassInstance);
类型:
C
这种方式extends Collection<B>
符合function color(element, color) {
element.style = "background: " + color + ";";
}
function wait(milliseconds)
{
var e = new Date().getTime() + (milliseconds);
while (new Date().getTime() <= e) {}
}
function disco() {
var body = document.getElementById("body")
color(body, "red")
wait(500)
color(body, "darkGreen")
wait(500)
color(body, "darkBlue")
}
disco()
,但方法签名不会那么优雅。
您还可以使用Reflection API获取type参数。这是example。
答案 2 :(得分:-1)
编译器警告您,因为该方法返回LinkedList.class
,但您期望List<TypeB>
的实例。如果没有编译器警告,您无法在Java中使用泛型。你无法做List<TypeB>.class
,它只是不起作用。 这是不可能的。
但是,以下代码是有效的案例:
<A, B, C> Collection<B> mapCollection(
final Iterable<A> source,
final Class<B> destinationClass,
final Class<C> collectionClass){...}
Collection<TypeB> = mapCollection(listOfA, TypeB.class, LinkedList.class);
这就是你可以使用泛型。