我有一个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()'函数。
答案 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子类。)