繁体   English   中英

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

[英]How do I convert from List<?> to List<T> in Java using generics?

在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);

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

如果列表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;
    }
}

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

笔记:

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

我会使用Guava及其Iterables.filter(Iterable,Class)方法以及Lists类中的工厂方法,如下所示:

List<?> original = ...;
List<String> typed = Lists.newArrayList(
   Iterables.filter(original, String.class));

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

为什么不传入你想要填充的空集合<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;
  }

那么调用代码看起来像:

  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 >>,则此方法不起作用。

番石榴再次,但如果它比一个演员或其他更复杂,允许更多的控制转换。

public List<L> convert(List<T> list) {
    return Lists.transform(list,new Function<T,L>() {

        public Object apply(T from) {

            L magic = (L)from;

            /* magic here */

            return magic;
        }});
}

您有运行时问题,因此它应该独立于泛型。 在运行时,无论如何,每个“都是一个对象”。 如果你不能实例化listClass ,那么你listClass传递了一个不提供(公共)空构造函数的java.util.List实现。

因此,您的问题的解决方案不在此方法之内。 称之为

 List<String> result = typedList(untypedList, String.class, ArrayList.class);

不应该给出运行时错误。


现在我手头有食。 以下代码编译,没有警告,应该满足您的要求:从无类型列表转换为类型列表。

public static <T> List<T> typedList(List<?> untypedList, Class<T> itemClass) {
  List<T> list = new ArrayList<T>();
  for (Object item : untypedList) {
    list.add(itemClass.cast(item));  // TODO - handle ClassCastExpception
  }
  return list;
}

Class.newInstance()方法抛出两个已检查的异常IllegalAccessExceptionInstantiationException 必须在方法的方法签名中捕获或声明它们。

为了记录,这些例外在各种情况下被抛出; 例如

  • 该类没有定义no-args构造函数
  • 构造函数不可见
  • 该类是抽象的或接口
  • class对象表示数组类型,基本类型或void“type”。

这实际上与该方法是通用的以及相关的键入问题无关。

我不相信你想做的事情是可能的。 这是因为泛型工作:

在编译时,将检查所有输入类型的类型化列表,并将所有传出对象转换为列表类型 - 从那时起,我们将讨论无类型“列表”。 遗憾的是,泛型只是语法糖。

你想达到什么目的? 这样的代码:

List l = new ArrayList();
l.add(new Integer(1));
List<Integer> li = l;

只是工作。 它产生警告,但有效。 但是,有可能在li你将拥有不是Integer实例的对象。 如果你想确定,请使用google guava,就像ColinD回答的那样。

请不要对这样的事情使用反射!

我会把它视为变换的一个例子。 也许是这样的

public static <D, S> transform(
    List<D> dst,
    List<S> src,
    Transformer<? extends D, ? super S> transformer
) { ... }

如果您愿意,可以使用List<>工厂。

如果你不想要警告,并且不想使用谷歌番石榴,你必须自己实现类似于番石榴的东西。 例如:

    private static <T> List<T> typeList(List<?> l, Class<T> klass) {
        List<T> list = new ArrayList<T>();
        for(Object obj : l) {
            if (klass.isAssignableFrom(obj.getClass())) {
                list.add((T) obj);
            }
        }
        return list;
    }

这个实现只是省略了不是T实例的元素,但你也可以抛出异常,或做任何你想做的事情。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM