【问题标题】:How to get value type of a map in Java? [duplicate]如何在 Java 中获取地图的值类型? [复制]
【发布时间】:2010-09-10 19:39:10
【问题描述】:

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

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

例如我想做类似的事情:

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

API 中当然没有这样的“getValueType()”函数。

【问题讨论】:

  • 你可以使用Reflection
  • 功能需求是什么?可能有比摆弄反射更好的解决方案。
  • 不,由于类型擦除,反射不会这样做。
  • @StaxMan - 实际上,反射起作用,因为您可以检索有关实例声明类型的信息。不过,您对安迪的回答的评论是正确的:如果您只有实例,则无法从中获取类型信息。但很可能该实例是在某处(类成员、方法签名等)声明的,并且该声明可用于反射性地确定类型。不过,从技术上讲,这些值并不能保证。

标签: java generics


【解决方案1】:

如果您的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 的cmets 应该注意。你真的不需要这样做; 已经声明了地图的类型,所以你没有得到比你已经拥有的更多的信息。见他的answer here

【讨论】:

  • 可以在运行时对映射中引用的值使用反射。
  • 需要注意的是,它只返回 declared 泛型类型(如 Map&lt;String, Integer&gt; map 部分),而不是 instantiated 泛型类型(如new HashMap&lt;String, Integer&gt;() 部分)。正如我在链接副本中的回答所指出的那样;如果您已经在代码中自己声明了它们,那么以这种方式计算它们是没有意义的......
  • @BalusC - 同意;您对有关存在设计替代方案的问题的评论可能是回答这个问题的更好方向。我的答案中的代码为 OP 提供的信息不超过他/她在编译时已经知道的信息。
【解决方案2】:

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

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

【讨论】:

  • 这不准确;有一些答案给出了执行此操作所需的确切代码,有几种不同的方式
  • 实际上,对于这种特殊情况,这是 100% 正确的:如果您所拥有的只是实例,则没有可查找的类型信息,什么也没有。有问题的 Map 是 HashMap 类型,因为这里的类型参数在编译后被丢弃(它们在字节码中引发必要的类型转换)。
【解决方案3】:

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

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

class MyStringMap extends HashMap<String,String> { }

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

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

被许多 Java 框架(Jersey、Guice、Jackson 等)使用

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

【讨论】:

    猜你喜欢
    • 2020-12-05
    • 2011-06-17
    • 1970-01-01
    • 2016-12-05
    • 1970-01-01
    • 1970-01-01
    • 2013-12-25
    • 2012-10-01
    • 2015-07-04
    相关资源
    最近更新 更多