【再论反射】通过反射了解java泛型本质          返回主页

开始本文之前先看一段代码

    ArrayList a1 = new ArrayList();
    ArrayList<String> a2 = new ArrayList<String>();

    Class c1 = a1.getClass();
    Class c2 = a2.getClass();

    a2.add("string1");
    a2.add("string2");
    System.out.println(c1 == c2);

问题:上述代码运行结果是什么?true or false?

也许您会说,应该是false,因为a1中可以放置任意数据类型而a2已经限定了是String泛型,所以是false。

很遗憾这种想法是错误的,运行结果是true。

那么为什么会造成这种结果呢?这就要从泛型的机理说起。

Java中的集合泛型是为了防止错误输入而设置的,它只在编译期有效,而一旦绕过编译期在运行期间就无效了。

也就是说,编译之后的集合的泛型是去泛型化的,亦即我们可以通过一些手段绕过使其无效。而这种手段就是反射

验证:我们可以通过方法的反射来绕过编译,从而绕过泛型。(关于反射请查看我写的相关博文,此处略过)。

代码如下:

    try {
        Method method = c2.getMethod("add", Object.class);
        method.invoke(a2, 20);
        System.out.println(a2);
    } catch (NoSuchMethodException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

运行结果为:[string1, string2, 20]

解释:我们通过获取类类型的方法类类型,使用invoke向原本只能添加String类型的a2中添加了一个整型数,并将其打印。表明在运行期使用方法反射确实可以绕过泛型的类型限制。

说明:使用反射方式添加的不同数据类型无法使用foreach遍历否则会抛出异常。

小结

综合上述我们得知,反射的操作都是在编译之后才运行的操作,而泛型在编译之后是去泛型化的,因此我们可以通过反射绕过泛型的类型限制。