使用泛型来转换Java集合的正确方法签名

时间:2016-11-02 09:47:55

标签: java generics collections

我正在努力确定使用泛型的正确方法签名,以便将类型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我宁愿避免。

有关如何解决此问题的任何想法?或者这不可能吗?

3 个答案:

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

这就是你可以使用泛型。