簡體   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