2013-06-04 63 views
1

我正在寫一個方法,用「Predicate」過濾特定集合,並返回一個僅包含過濾元素的新集合(Predicate返回true的集合)。如何在運行時創建通用集合<T>?

事情是這樣的:

public <T> Collection<T> filter(Collection<T> collection, Closure<T> predicate); 

我知道,在Java中,我不能只在運行時創建一個新的Collection(),因爲類型擦除的。

我也通過向方法傳遞一個額外參數來調用T.newInstance()來了解「解決方法」。

這看起來像:

public <T> Collection<T> filter(Class<? extends Collection<T>> collectionToInstanciate, Collection<T> collection, Closure<T> predicate) { 

     // create the new Collection 
     Collection<T> container = collectionToInstanciate.newInstance(); 

     // and then add only filtered items 
     Iterator<T> iter = collection.iterator(); 
     while (iter.hasNext()) { 
      T obj = iter.next(); 

      // if Predicate.invoke() returns true, then keep element, otherwise skip it 
      if (predicate.invoke(obj)) { 
       container.add(obj); 
      } 
     } 
     return container; 
    } 

但我應該怎麼叫我的方法是什麼?

舉例來說,如果我想只有一列整數的奇數,我想要做的:

// instanciate ArrayList<Integer> = [1, 2, 3, 4, 5] 
    ArrayList<Integer> array = ...; 

    // return a new LinkedList<Integer> with only odd numbers 
    filter(LinkedList<Integer>.class, array, new Closure<Integer>() { 
     public Boolean invoke(Integer arg_p) { 
      return (arg_p % 2 == 0); 
     } 
    }); 

    // should return [2, 4] as a LinkedList<Integer> 

的問題是,

LinkedList<Integer>.class 

不能編譯。

我該如何聲明,以正確instanciate過濾器()方法LinkedList?

問候,

+0

也許我錯了,但不能傳遞MyClass.class並將其用作方法內部的LinkedList參數? – Quirin

+0

請注意,「集合」是一個接口,而不是一個類。順便說一句,由於類型擦除,您應該使用原始類,而不是參數化的方式,即'LinkedList.class'而不是'LinkedList .class'。 –

+0

@LuiggiMendoza:使用LinkedList.class而不是LinkedList .class實際上是我的問題:我不想在filter()方法中實例化一個「原始」LinkedList,所以我會得到Peter Lawrey的解決方案。謝謝 –

回答

3

泛型是一個編譯時功能,並擁有在運行時沒有什麼意義。如果你想創建一個LinkedList,那就是你所做的。你不能讓編譯器根據你在運行時做的事情給你一個錯誤。

更簡單的解決方案是傳遞您想要填充的類的實例。

List<Integer> result = filter(new LinkedList<Integer>(), array, 
    new Predicate<Integer>() { 
     public boolean invoke(Integer arg_p) { 
      return (arg_p % 2 == 0); 
     } 
    }); 

它稍微短一點,可以在編譯時檢查。

注意:這些謂詞中的很多作爲一個簡單的循環要簡單得多,速度更快。

List<Integer> result = new LinkedList<Integer>(); 
for(Integer i: array) 
    if (i % 2 == 0) 
     result.add(i); 
+0

感謝您的解決方案。我希望泛型會更強大,但由於類型擦除,我不能寫一個「C++風格」的模板:( –

+0

@ adrien.pain)Java在表達力的簡單性方面往往會犯錯。這確實意味着閱讀其他人的代碼更容易,因爲邊緣案例的機會較少,寫同一事物的方式也不盡相同。 –

+1

是的,我同意這一點,但我不喜歡類型擦除;-) –

0

身邊的另一種方式,從彼得Lawrey答案,並LuiggiMendoza的評論,是利用過濾器()方法是這樣的:

List<Integer> result = filter(
     LinkedList.class, // instead of new LinkedList<Integer>() 
     array, 
     new Predicate<Integer>() { 
      public boolean invoke(Integer arg_p) { 
       return (arg_p % 2 == 0); 
      } 
     }); 

,在過濾()方法:

public <T> Collection<T> filter(Class<? extends Collection> collectionToInstanciate, Collection<T> collection, Closure<T> predicate) { 

    // create the new Collection as a raw Collection, and cast it back to Collection<T> 
    Collection<T> container = (Collection<T>) collectionToInstanciate.newInstance(); 

    // and then add only filtered items 
    Iterator<T> iter = collection.iterator(); 
    while (iter.hasNext()) { 
     T obj = iter.next(); 

     // if Predicate.invoke() returns true, then keep element, otherwise skip it 
     if (predicate.invoke(obj)) { 
      container.add(obj); 
     } 
    } 
    return container; 
} 
+0

我可以想象這種方法對於沒有0-arg構造函數的集合實現來說非常糟糕。你可以包含一個工廠參數來避免所有這些無謂的反射,並定義一些處理常見情況的變體(如'filterToLinkedList') –

+0

@JudgeMental:true,會更好! –