如何在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子类)。