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

时间:2010-09-10 19:39:10

标签: java generics

  

可能重复:
  Get generic type of java.util.List

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

e.g。我想做点什么:

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

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

3 个答案:

答案 0 :(得分:14)

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

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();
        }
    }
}

上面的代码将打印出来:

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的评论。你真的不应该这样做; 已经宣布了Map的类型,因此你没有获得比现有更多的信息。见他的answer here

答案 1 :(得分:7)

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

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

答案 2 :(得分:2)

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

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

class MyStringMap extends HashMap<String,String> { }

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

  new TypeToken<Map<String, Double>>() { }
许多Java框架(Jersey,Guice,Jackson等)使用

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