泛型混淆:欺骗编译器

时间:2014-09-30 20:56:12

标签: java generics

考虑一下代码:

public class GenericsConfusion {
    public static <T> Class<T> get(Class<T> clazz) {
        Map<Class, Class> map = new HashMap<Class, Class>();
        map.put(Integer.class, String.class);
        return map.get(clazz);
    }

    public static void main(String[] args) {
        Class<Integer> clazz = get(Integer.class);
        System.out.println(clazz);
    }
}

它编译并运行完美。我们的想法是在get方法中返回与输入类具有相同类型参数的类。但由于地图的存在,它被打破了。 是的,我知道在运行时会删除类型参数信息,因此如果没有类型参数,则此代码完全有效。另外我知道我可以通过指定Map<Class<T>, Class<T>>来修复它但事实是在方法签名中我有类型参数,它们在编译时没有帮助我。

这是对某些概念的误用吗?

或者它是Java泛型的缺点?

或者它完全可以,我误解了类型参数的想法?

2 个答案:

答案 0 :(得分:8)

原始类型,例如ClassMap(与Class<...>Map<..., ...>相对),规避了类型检查仿制药。你甚至可以这样写:

final Class<Integer> whoops = (Class) String.class;

这是类型系统中一个令人遗憾的弱点。它最初包含在Java 5(引入泛型)中,与先前版本中编写的代码兼容。

在大多数情况下,您可以通过避免使用原始类型来避免这种弱点。你的编译器应该警告你。

不幸的是,在各种情况下,原始类型基本上是不可避免的(由于.getClass()的特殊输入;由于我们只能编写(例如)Map.class而不是{{ 1}}(或Map<String, String>.class);由于擦除和反射等原因);但幸运的是,正如你所指出的,你的情况似乎并不是其中之一。

答案 1 :(得分:0)

我猜你要从地图上归还一些东西?

目前,理论上你可以在地图中存储除Class<T>之外的其他内容,例如Class<Y>,但不是来自此代码,而是泄漏引用等。要将地图中的内容作为{{1}返回您必须强制地图仅存储Class<T>而不存储任何其他内容。

目前,它可以在我相信的同时存储Class<T>Class<T>