黑马程序员技术交流社区

标题: 通过反射验证泛型只是给编译器使用的问题 [打印本页]

作者: 徐帅    时间: 2012-11-30 16:56
标题: 通过反射验证泛型只是给编译器使用的问题
本帖最后由 徐帅 于 2012-12-1 16:32 编辑

在张老师的泛型中,验证泛型只是给编译器使用的,在生成字节码的时候会去泛型,并通过反射来验证了这一点,
但我在使用反射验证的时候,泛型限定为Integer时,通过反射存入字符串可以,但是当泛型限定为String时,存入int类型的
数据,会出现异常:java.lang.Integer cannot be cast to java.lang.String,不知道如何解释?请大侠帮忙看看
  1. package cn.xushuai.test;

  2. import java.lang.reflect.Constructor;
  3. import java.util.ArrayList;

  4. public class Geniric {
  5.          public static void main(String[] args) throws Exception{
  6.                 ArrayList collection = new ArrayList();
  7.                 collection.add(1);
  8.                 collection.add(1L);
  9.                 collection.add("abc");
  10.                 //int i = (Integer)collection.get(1);
  11.         
  12.                 //反射中使用泛型
  13.                 Constructor<String> constructor = String.class.getConstructor(StringBuffer.class);
  14.                 String str = constructor.newInstance(new StringBuffer("abc"));//无需强转
  15.                 System.out.println(str.charAt(2));
  16.                
  17.                 //验证泛型仅是给javac编译器使用的,编译生成的字节码会去掉泛型的类型信息
  18.                 ArrayList<Integer> collection2 = new ArrayList<Integer>();
  19.                 ArrayList<String> collection3 = new ArrayList<String>();
  20.                 System.out.println(collection2.getClass() == collection3.getClass());        //true
  21.                
  22.                 //在Integer的泛型限定下,存入字符串
  23.                 collection2.getClass().getMethod("add", Object.class).invoke(collection2, "haha");
  24.                
  25.                 //在String的泛型限定下,存入int
  26.                 collection3.getClass().getMethod("add", Object.class).invoke(collection3, 9090);
  27.                 System.out.println(collection2.get(0));//haha
  28. <font color="#ff0000">                System.out.println(collection3.get(0));//java.lang.Integer cannot be cast to java.lang.String</font>
  29.                
  30.          }
  31. }
复制代码

作者: 冯盼    时间: 2012-11-30 17:07
没注意过这个问题,也想知道。。。
作者: 张学永    时间: 2012-11-30 17:18
我刚刚学到这里,等会试试。
作者: 黑马_郑亮新    时间: 2012-11-30 17:47
还没看到这里
作者: 徐帅    时间: 2012-11-30 17:48
这里的get方法也用反射的方式调用,结果可以正确打印出:9090,代码如下:
  1. int obj =(Integer) collection3.getClass().getMethod("get", int.class).invoke(collection3, 0);
  2.                 System.out.println(obj);
复制代码
不知道为我上面的程序中,使用Integer泛型限定可以存入String类型的数据,而且可以正确打印,
而使用String泛型限定时存入Integer类型的数据,却要使用反射的方式调用方法才可以打印出结果呢,
还是不太明白啊,请大家帮忙解答,十分感谢!
作者: 张学永    时间: 2012-11-30 19:11
本帖最后由 张学永 于 2012-11-30 19:13 编辑

这个我刚刚看完,说说我的理解吧。
在这里一个可以打印,一个不可以打印。我觉得原因在这里:
首先,两种情况都存进去了,这是肯定的。但是取出来的情况就比较复杂了。
我认为在取的时候,如果给取出来的元素加一些引用,或者是取出来的元素调用某些方法。虚拟机都会去把取出来的元素去和泛型限定的类型相匹配,这也就是为什么使用了泛型限定后不用强制转换的原因。
而println()这个方法,有重载形式,一种是println(String str),一种是println(Object obj),你取出来的是String类型的,则会调用第一种打印方法,直接打印出字符串。而当你取出来的是Integer对象的时候,会调用第二种方式。而第二种方式,在API中的描述是返回的是obj.toString(),也就是说这里用到了Integer.toString(),但是Integer是你从集合里取出来的,当它去调用方法的时候,虚拟机就会自动去和泛型匹配,而泛型为String,这里就会报错了。所以取出来的元素只有进行了转换才可以去调用方法。不信你可以试试用你可以打印的那个元素(取出的那个字符串元素)去调用toString()方法再打印,就会报错。
作者: 徐帅    时间: 2012-11-30 19:13
本帖最后由 徐帅 于 2012-11-30 19:14 编辑
徐帅 发表于 2012-11-30 17:48
这里的get方法也用反射的方式调用,结果可以正确打印出:9090,代码如下:不知道为我上面的程序中,使用Int ...

你说的Integer可以调用toString方法打印,我能明白,但后面半句不是很清楚,String类型的toString是接收Object类型?我查API没有发现参数,toString方法一般不都是通过复写的么?我通过明确调用toString方法,得到下面的结论:

如果我们已经使用过泛型,而通过反射又存入不同类型的数据时,当我们显示的调用toString方法,都会出现异常,原因是开发工具为我们制定的toString方法,并不是我们期望的,而是根据泛型限定中的制定数据类型来调用该类型的toString()方法,而对于上面的Integer类型中存储String类型可以成功打印的原因是:
System.out.println(collection2.get(0));//haha,这里的返回结果按照泛型是Integer类型,所以可以调用Integer的toString()打印。
所以结论就是:我们在程序中调用toString()方法时,开发工具会为我们调用指定泛型限定类型的toString()方法下面是验证代码:

  1. System.out.println(collection2.get(0));//正确打印"haha",这里的结果按照泛型是Integer类型,因为是对象,所以可以调用toString()打印
  2. //将鼠标移上去时,会发现调用的toString()是Integer类型的,所以会把"haha"当做Integer,因此报转换异常
  3.                 System.out.println(collection2.get(0).toString());//java.lang.String cannot be cast to java.lang.Integer
  4.                
  5.                 //将鼠标移上去时,会发现调用的toString()是String类型的,所以会把9090看做String类型,因此报转换异常
  6.                 System.out.println(collection3.get(0).toString());//java.lang.Integer cannot be cast to java.lang.Stri
复制代码
不知道说的对不对,还希望你能把”但String里边的toString不能输出Integer的,那传递的是Object对象,那样调用是有泛型限定的“这句话解释的更详细些,十分感谢:)
作者: 徐帅    时间: 2012-11-30 19:18
张学永 发表于 2012-11-30 19:11
这个我刚刚看完,说说我的理解吧。
在这里一个可以打印,一个不可以打印。我觉得原因在这里:
首先,两种情 ...

你和我想的一样,下面是我的帖子,刚贴完,就看到你的帖子了,是啊,程序中调用toString()方法时,开发工具会为我们调用指定泛型限定类型的toString()方法,所以会出现类型转换异常,而我们直接获取到Integer时,
会自动调用toString方法:handshake
作者: 张学永    时间: 2012-11-30 19:20
徐帅 发表于 2012-11-30 19:18
你和我想的一样,下面是我的帖子,刚贴完,就看到你的帖子了,是啊,程序中调用toString()方法时,开发工 ...

:#同时发出来了。。。打了半天,一看你明白了。。。带着你的问题看的这一集。你发问题的时候正好看到这里。




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2