2010-09-10 63 views
10

可能重複:
Get generic type of java.util.List如何在Java中獲取地圖的值類型?

我有一個地圖,我想從地圖的一個實例得到T的類型。我怎樣才能做到這一點?

例如我想做類似的事情:

Map<String, Double> map = new HashMap<String, Double>(); 
... 
String vtype = map.getValueType().getClass().getName(); //I want to get Double here 

當然,在API中沒有這種'getValueType()'函數。

+4

看到這個:http://stackoverflow.com/questions/1942644/get-generic-type-of-java-util-list – 2010-09-10 19:41:54

+0

你可以使用[Reflection](http://download.oracle.com/javase /tutorial/reflect/index.html) – Aillyn 2010-09-10 19:44:01

+0

什麼是功能要求?可能有更好的解決辦法,而不是擺弄反思。 – BalusC 2010-09-10 20:03:05

回答

8

無法從實例中獲取它,因爲在Java泛型中,類型參數僅在編譯時可用,而不是在運行時可用。

這就是所謂的類型擦除。

import java.lang.reflect.*; 
import java.util.*; 

public class Generic { 
    private Map<String, Number> map = new HashMap<String, Number>(); 

    public static void main(String[] args) { 
     try { 
      ParameterizedType pt = (ParameterizedType)Generic.class.getDeclaredField("map").getGenericType(); 
      for(Type type : pt.getActualTypeArguments()) { 
       System.out.println(type.toString()); 
      } 
     } catch(NoSuchFieldException e) { 
      e.printStackTrace(); 
     } 
    } 
} 

上面的代碼將打印:更正式的定義在JLS.

+2

這不是準確的;有些答案給出了需要做的確切代碼,有幾種不同的方式 – 2010-09-10 20:02:34

+2

實際上,對於這種特殊情況,這是100%正確的:如果你擁有的只是實例,那麼沒有任何類型信息可以找到,完全沒有。有問題的地圖類型是HashMap,因爲這裏的類型參數在編譯後被丟棄(它們會引發必要的字節代碼類型轉換)。 – StaxMan 2010-09-10 20:36:59

12

如果Map聲明場,你可以做以下提供

class java.lang.String 
class java.lang.Number 

但是,如果您僅擁有Map的實例,例如如果它被傳遞給方法,你不能在那個時候得到的信息:

public static void getType(Map<String, Number> erased) { 
    // cannot get the generic type information directly off the "erased" parameter here 
} 

可以搶方法簽名(再次使用像我上面的例子反射)來確定的erased類型,但它並不真正讓你在任何地方(見下面的編輯)。

編輯:

BalusC的下面的評論應注意。你真的不需要這樣做; 已經聲明瞭地圖的類型,所以你沒有得到比以前更多的信息。看到他的answer here

+0

反射可以在運行時用於地圖中引用的值。 – 2010-09-10 19:59:31

+4

注意應該只返回**聲明的**泛型類型(如'Map map'部分),而不是**實例化的泛型類型(如'new HashMap ()'部分)。正如我在鏈接副本中的答案所指出的那樣;如果你已經在代碼中自己聲明它們,那麼沒有必要這樣判斷它們...... – BalusC 2010-09-10 20:07:08

+0

@BalusC - 同意;你對有關設計方案的問題的評論可能是採取這個問題的更好方向。我的回答中的代碼給OP提供的信息比編譯時已經知道的更多。 – 2010-09-10 20:18:14

1

雖然給出了正確的答案,但還有一個尚未指出的替代方案。基本上,字段和方法不是泛型類型信息可以存在的唯一地方:超類聲明也包含這些信息。

這意味着,如果你的地圖實際上是一個子類,如:

class MyStringMap extends HashMap<String,String> { } 

則可以通過調用「getGenericSuperclass」(在「instance.getClass找出中使用的泛型類型參數, ()'),然後訪問分配的實際類型參數。它確實得到了相當的參與,因爲必須遍歷類型層次結構以確保參數正確綁定到Map(可能是別名等),但它可以完成。這是這樣的「超級令牌」被用來傳遞泛型聲明,如:

new TypeToken<Map<String, Double>>() { } 

這是被許多Java框架(澤西島,吉斯,傑克遜以及更多)這裏

問題是,雖然這確實允許確定一個實例的名義類型,它只在存在實際非泛型泛型類的子類時才起作用,並且我不知道如何強制執行這個要求(調用代碼可能覺得它很奇怪只是爲了這個目的創建一個有點虛假的Map子類)。