簡體   English   中英

在泛型方法中創建通用數組實例

[英]Creating a generic array instance in a generic method

我正在嘗試構建一個幫助方法,將兩個行列表轉換為數組轉換為一行。 我遇到的問題是我不確定如何創建一個T []實例。

我試過了

Array.newInstance(T.class, list.size)但是我無法為它提供T.class ..

還嘗試了new T[](list.size)但它不喜歡參數。

public <T> T[] ConvertToArray(List<T> list)
{
   T[] result = ???

   result = list.toArray(result);

   return result;
}

還有其他想法嗎?

謝謝

你不能混淆泛型和那樣的數組。 泛型具有編譯時檢查,數組具有運行時檢查,並且這些方法大多不兼容。 起初我建議:

@SuppressWarnings("unchecked")
public <T> T[] ConvertToArray(List<T> list)
{      
   Object[] result = new Object[list.size()];
   result = list.toArray(result);
   return (T[])result;
}

這是一種隱秘的錯誤,因為至少有一個其他人認為它會起作用! 但是,當您運行它時會出現不兼容的類型錯誤,因為您無法將Object []強制轉換為Integer []。 為什么我們不能獲得T.class並創建一個正確類型的數組? 或者做new T[]

泛型使用類型擦除來保持向后兼容性。 它們在編譯時檢查,但從運行時剝離,因此字節碼與預泛化JVM兼容。 這意味着您無法在運行時獲得泛型變量的類知識!

因此,雖然您可以保證T[] result將提前為Integer []類型,但代碼list.toArray(result); (或new T[] ,或者Array.newInstance(T.class, list.size()); )只會在運行時發生,而且它不知道T是什么!

下面是工作,作為報酬,用於讀取講座版本:

public static <T> T[] convertToArray(List<?> list, Class<T> c) {
    @SuppressWarnings("unchecked")
    T[] result = (T[]) Array.newInstance(c, list.size());
    result = list.toArray(result);
    return (T[]) result;
}

請注意,我們有第二個參數在運行時(以及通過泛型編譯時)提供類。 你會像這樣使用它:

Integer[] arrayOfIntegers = convertToArray(listOfIntegers, Integer.class);

這值得麻煩嗎? 我們仍然需要抑制警告,所以它絕對安全嗎?

我的回答是肯定的。 生成的警告只是來自編譯器的“我不確定”。 通過單步執行,我們可以確認該轉換將始終成功 - 即使您將錯誤的類作為第二個參數,也會拋出編譯時警告。

這樣做的主要優點是我們已將警告集中到一個地方。 我們只需要證明這一個地方是正確的,我們知道代碼將永遠成功。 引用Java文檔:

該語言旨在保證如果您的整個應用程序使用javac -source 1.5編譯時沒有未經檢查的警告,則它是類型安全的[1]

所以現在不是在你的代碼中都有這些警告,而是只在一個地方,你可以使用它而不必擔心 - 通過使用它可以大大降低你犯錯誤的風險。

您可能還想看看這個SO答案 ,它更深入地解釋了這個問題, 這個答案是我寫這篇文章時的床單。 除了已經引用的Java文檔之外 ,我使用的另一個方便的參考是Neal Gafter的博客文章 ,他是Sun Microsystems的高級工程師,以及1.4和5.0語言功能的共同設計師。

當然,感謝ShaneC正確地指出我的第一個答案在運行時失敗了!

如果你不能傳入T.class ,那么你基本上是搞砸了。 類型擦除意味着您在執行時根本不知道T的類型。

當然還有其他指定類型的方法,比如超類型令牌 - 但我的猜測是,如果你不能傳入T.class ,你也無法傳入類型令牌。 如果可以,那就太好了:)

問題是由於類型擦除,List在運行時不知道它的組件類型,而組件類型是創建數組所需的類型。

因此,您可以在API中找到兩個選項:

我能想到的唯一另一種可能性是一個巨大的麻煩:

  • 迭代列表中的所有項目並找到“最大公約數”,這是所有項目擴展或實現的最具體的類或接口。
  • 創建該類型的數組

但這將比你的兩行更多(並且它也可能導致客戶端代碼做出無效的假設)。

這真的需要改成單行嗎? 對於任何具體類,您可以在一行中執行List to Array轉換:

MyClass[] result = list.toArray(new MyClass[0]);

當然,這不適用於類中的泛型參數。

參見Joshua Bloch的Effective Java Second Edition ,第25項:首選列表到數組(第119-123頁)。 這是PDF示例章節的一部分。

這是我能看到的最接近你想要的東西。 這使得您在編寫代碼時知道類的大假設以及列表中的所有對象都是同一個類,即沒有子類。 我確信這可以變得更復雜,以減輕沒有子類的假設,但我不知道如何在Java中你可以繞過在編碼時知道類的假設。

package play1;

import java.util.*;

public class Play
{
  public static void main (String args[])
  {
    List<String> list=new ArrayList<String>();
    list.add("Hello");
    list.add("Shalom");
    list.add("Godspidanya");

    ArrayTool<String> arrayTool=new ArrayTool<String>();
    String[] array=arrayTool.arrayify(list);

    for (int x=0;x<array.length;++x)
    {
      System.out.println(array[x]);
    }
  }
}
class ArrayTool<T>
{
  public T[] arrayify(List<T> list)
  {
    Class clazz=list.get(0).getClass();
    T[] a=(T[]) Array.newInstance(clazz, list.size());
    return list.toArray(a);
  }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM