如何使用泛型从Java中的List <?>转换为List <T>?

Der*_*har 15 java generics list covariance

在Java中,如何转换List<?>List<T>使用通用方法,以便我可以使用单个方法调用替换以下模式:

List untypedList = new ArrayList();  // or returned from a legacy method
List<Integer> typedList = new ArrayList<Integer>();
for (Object item: untypedList)
    typedList.add((Integer)item);
Run Code Online (Sandbox Code Playgroud)

请注意,上面的代码不会生成任何类型安全警告,理想情况下,您的解决方案也不应生成任何此类警告.

如果列表Class<L>具有公共默认构造函数,以下解决方案是否可行?

public class ListUtil {
    public static <T, L extends List<T>> L typedList(List<?> untypedList, Class<T> itemClass, Class<L> listClass) {
        L list = null;
        try {
            list = listClass.newInstance();
        } catch (InstantiationException e) {
        } catch (IllegalAccessException e) {
        }
        for (Object item: untypedList)
            list.add(itemClass.cast(item));
        return list;
    }
}
Run Code Online (Sandbox Code Playgroud)

(注意listClass.newInstance()抛出InstantiationException或者IllegalAccessException如果实例Class<L>没有公共默认构造函数.如果方法没有正确处理这些异常,可能会出现什么问题?)

笔记:

  • T 是结果列表中每个项目的类型.
  • L是我想要创建的列表的类型(扩展List<T>).
  • untypedList是"无类型"输入列表,实际上是相同的List<Object>.
  • itemClass表示运行时类T.
  • listClass表示运行时类L.

Col*_*inD 13

我会使用番石榴和其Iterables.filter(可迭代,班)方法用一个工厂方法沿着从Lists类,如下所示:

List<?> original = ...;
List<String> typed = Lists.newArrayList(
   Iterables.filter(original, String.class));
Run Code Online (Sandbox Code Playgroud)

这将实际检查原始列表中的每个对象,结果列表将仅包含给定类型(String在本例中)的实例或其子类型的那些元素.我真的认为让用户为Class结果List类型提供一个并尝试通过反射实例化它是没有意义的.


Mic*_*l D 8

为什么不传入你想要填充的空集合<T>,而不是传入你想要实例化的列表类型?这为api的用户提供了更大的灵活性,因为使用默认构造函数并不总是理想的.(例如,也许我想要一个Set,我提供预期数量的元素,或者我想要一个排序列表,我提供比较器).

另外,作为附注,您应该始终编程到最通用的接口.在这种情况下,您的输入不需要比Iterable更具体,输出集合.

鉴于此,我会用这种方式编写方法 -

  public static <T, C extends Collection<T>> C typesafeAdd(Iterable<?> from, C to, Class<T> listClass) {
    for (Object item: from) {
      to.add(listClass.cast(item));
    }
    return to;
  }
Run Code Online (Sandbox Code Playgroud)

那么调用代码看起来像:

  public static void main(String[] args) {
    List<?> untypedStringList = LegacyApi.getStringList();
    List<String> typesafeStringList = typesafeAdd(untypedStringList, new ArrayList<String>(), String.class);
  }

2评论在这里:

  • 如果你真的可以信任LegacyApi(或任何为你提供无类型列表的东西)只返回一个具有预期类型的​​集合,那么你可以只做一个未经检查的强制转换并禁止它.这应该局限于可能的最小范围.即:创建类似TypesafeLegacyApiWrapper的东西,它将调用委托给LegacyApi.
  • 如果你有更复杂的东西,这种方法签名仍然会崩溃.例如,如果您有List <List <String >>,则此方法不起作用.

  • 我不建议尝试反射性地实例化新列表.这将非常容易出错,并会让您头疼.我显然不知道你的问题的整个范围,但我会考虑编写一个围绕API的包装器,为你提供无类型列表,它对列表的元素进行完整性检查,然后返回列表与未经检查的演员表.(这是甚至引入未经检查的演员的能力的原因之一...有合法的案例,特别是在处理前仿制品apis时). (2认同)