如何检查地图类型是否扩展为

时间:2017-08-30 14:26:47

标签: java generics reflection types

我正在尝试检查Map<String, T>是否包含Double或Integer类型的Objects(T)。

我不想使用地图中的实际对象来检查类,因为它不确定Map是否包含对象。

我可以通过执行以下操作来实现此目的(假设该字段包含Map):

ParameterizedType type = (ParameterizedType) field.getGenericType(); 

isNumeric(type);

/**
 * @param type
 * @return Returns true if type is numeric
 */
private static boolean isNumeric(ParameterizedType type) {
    return  type.getActualTypeArguments()[1].toString().equals("? extends java.lang.Number");
}

这对我来说已经足够了,但它并不像是一个干净的解决方案。

但是,我可以检索getActualTypeArguments()[1]的类型 通过执行以下操作:

Type typeOfSecondGeneric = type.getActualTypeArguments()[1]; // equals '? extends java.lang.Number'

我无法使用Number.class.isAssignableFrom(typeOfSecondGeneric); // Class expected

在我研究了一下之后,我没有想出比做String比较更好的解决方案。

我出错了什么?

非常感谢帮助!

2 个答案:

答案 0 :(得分:1)

临时解决方案可能是这样的:

private static boolean isNumericValue(ParameterizedType t) {
    return isSubclassOf(t.getActualTypeArguments()[1], Number.class);
}

private static boolean isSubclassOf(Type t, Class<?> clazz) {
    if (t instanceof Class<?>)
        return clazz.isAssignableFrom((Class<?>) t);
    if (t instanceof ParameterizedType)
        return isSubclassOf(((ParameterizedType) t).getRawType(), clazz);
    Type[] bounds = null;
    if (t instanceof TypeVariable<?>)
        bounds = ((TypeVariable<?>) t).getBounds();
    if (t instanceof WildcardType)
        bounds = ((WildcardType) t).getUpperBounds();
    if (bounds != null && bounds.length > 0)
        return isSubclassOf(bounds[0], clazz);
    return clazz == Object.class;
}

这个简短的例子不处理接口类型或通用数组类型。在实际的子类型规则方面,它还远没有完成。

如果确实需要这样的话,我认为更好的解决方案是使用Guava TypeToken

private static final TypeToken<Map<?, ? extends Number>> T =
    new TypeToken<Map<?, ? extends Number>>() {};

private static boolean isNumericValue(ParameterizedType t) {
    return T.isSupertypeOf(t);
}

那就是说,我并不是所有人都相信你确实需要这个。这看起来像XY problem,您可能会更好地询问有关您尝试解决的问题的问题,而不是您尝试的解决方案。这种代码在你的程序被束缚到使用它之后会变成一个巨大的痛苦,特别是如果它普遍存在的话。

答案 1 :(得分:1)

此信息在运行时丢失,因此通常您无法确定T到底是什么。

此一般规则的例外仅限于地图对象类本身对该T具有更具体约束的情况。例如:

class MyDoubleMap<K> extends Map<K, Double> {
...
}    
...
Map<String, Double> standard = new HashMap<>();
Map<String, Double> doubles = new MyDoubleMap<>();
Map<String, Double> annonDoubles = new HashMap<>() {};

使用standard任何反射技巧都无法恢复超过T,并且它可以是任何扩展Object的内容。

但是对于其他两个案例doublesannonDoubles,其他答案中包含的反映代码应该Double分配到T

使用doubles应该很容易看到该信息应该通过反射提供,因为它是MyDoubleMap声明的组成部分。也许 annonDoubles稍微不那么明显,但实际上跟踪{}它实际上是在宣布匿名 扩展HashMap<String, Double>的内部类,因此可以通过反射恢复T(以及K)的此类信息。