代码之家  ›  专栏  ›  技术社区  ›  Derek Mahar

如何从列表转换?>使用泛型在Java中列出<t>?

  •  15
  • Derek Mahar  · 技术社区  · 14 年前

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

    (注意 listClass.newInstance() 投掷 InstantiationException IllegalAccessException 如果 类& LT & GT; 没有公共默认构造函数。如果方法不能正确处理这些异常,会出现什么问题?)

    笔记:

    • T 是结果列表中每个项的类型。
    • L 是我要创建的列表的类型(扩展到 列表& T; )
    • untypedList “非类型化”输入列表是否与 List<Object> .
    • itemClass 表示的运行时类 T .
    • listClass 表示的运行时类 L .
    9 回复  |  直到 14 年前
        1
  •  8
  •   Michael D    14 年前

    与其传入要实例化的列表类型,不如直接传入要填充的空集合<t>?这给了API的用户更多的灵活性,因为使用默认的构造函数并不总是理想的。(例如,也许我想要一个我提供预期元素数量的集合,或者我想要一个排序列表,我在其中提供比较器)。

    另外,作为补充说明,您应该始终编程到尽可能通用的接口。在这种情况下,输入只需要一个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(或任何提供给您的非类型化列表)只返回一个包含预期类型的集合,那么您只需执行未选中的强制转换并抑制它。这应该被局限在尽可能小的范围内。IE:创建类似于typesafelegacyapipwrapper的东西,委托调用legacyapi。
    • 如果您有更复杂的东西,这个方法签名仍然会失效。例如,如果您有列表,则此方法不起作用。
        2
  •  13
  •   ColinD    14 年前

    我会用 Guava 及其 Iterables.filter(Iterable,Class) 方法和工厂方法 Lists 类,像这样:

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

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

        3
  •  3
  •   BjornS    14 年前

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

    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;
            }});
    }
    
        4
  •  2
  •   Andreas Dolk    14 年前

    您有一个运行时问题,所以它应该独立于泛型。在运行时,任何时候“都是一个对象”。如果无法实例化 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;
    }
    
        5
  •  1
  •   Stephen C    14 年前

    这个 Class.newInstance() 方法引发两个已检查的异常 IllegalAccessException InstantiationException . 这些必须在方法的方法签名中捕获或声明。

    对于记录,这些异常会在各种情况下抛出;例如

    • 该类未定义任何参数构造函数
    • 构造函数不可见
    • 类是抽象的或接口
    • 类对象表示数组类型、基元类型或void“类型”。

    这实际上与这个方法是通用的以及相关的类型问题无关。

        6
  •  0
  •   Jasper    14 年前

    我不相信你想做的是可能的。这是因为仿制药的作用:

    在编译时,将检查类型化列表的所有输入类型,并将所有输出对象强制转换为列表的类型,从那时起,我们将讨论一个非类型化的“列表”。不幸的是,泛型只是句法上的糖分。

        7
  •  0
  •   amorfis    14 年前

    你想实现什么?这样的代码:

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

    只是工作。它会产生警告,但会起作用。然而,有可能在 li 您的对象不是 Integer . 如果你想确定的话,使用谷歌的番石榴,就像科林德回答的那样。

        8
  •  0
  •   Tom Hawtin - tackline    14 年前

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

    我会把它当作一个转换的例子。也许有点像

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

    使用工厂 List<> 如果你喜欢的话。

        9
  •  0
  •   amorfis    14 年前

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

        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;
        }
    

    这个实现只是commits元素,它不是t的实例,但是您也可以抛出异常,或者做任何您想要做的事情。