如何在Java中获取地图的值类型?

可能重复:
获取java.util.List的generics类型

我有一个Map,我想从该Map的实例中获取T的类型。 我怎样才能做到这一点?

例如,我想做类似的事情:

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

当然,API中没有这样的’getValueType()’函数。

您无法从实例中获取它,因为在Javagenerics中,type参数仅在编译时可用,而不是在运行时。

这被称为类型擦除。 JLS中提供了更正式的定义。

如果您的Map声明的字段 ,则可以执行以下操作:

 import java.lang.reflect.*; import java.util.*; public class Generic { private Map map = new HashMap(); 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(); } } } 

上面的代码将打印:

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

但是,如果您只是拥有Map的实例,例如,如果它被传递给方法,那么您当时无法获取信息:

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

可以获取方法签名(再次使用reflection,如上面的示例)来确定已erased的类型,但它并没有真正让您到任何地方(请参阅下面的编辑)。

编辑:

应注意以下BalusC的评论。 你真的不应该这样做; 已经宣布了Map的类型,所以你没有得到比现有更多的信息。 在这里看到他的答案 。

虽然已经给出了正确的答案,但还有一个尚未指出的替代方案。 基本上,字段和方法不是generics类型信息可以存在的唯一地方:超类声明也具有此信息。

这意味着如果你的Map实际上是一个子类型,如:

 class MyStringMap extends HashMap { } 

然后可以通过调用’getGenericSuperclass’(在’instance.getClass()’上),然后访问分配的实际类型参数来找出所使用的generics类型参数。 它确实非常复杂,因为必须遍历类型层次结构以确保参数适当地绑定到Map(可能是别名等),但它可以完成。 这就是“超级令牌”用于传递通用声明的方式,如:

  new TypeToken>() { } 

许多Java框架(Jersey,Guice,Jackson等)都使用它

这里的问题是虽然这确实允许确定一个实例的名义类型,但它只有在存在generics类型的实际非generics子类时才有效,并且我不知道如何强制执行此要求(调用代码可能会找到奇怪的是它必须为此目的创建一个有点虚假的Map子类)。