黑马程序员技术交流社区

标题: 反射用到泛型中 [打印本页]

作者: 编程的梦想    时间: 2013-11-14 20:26
标题: 反射用到泛型中
package generic;

import java.lang.reflect.Method;
import java.util.*;

class GenericDemo {

        /**
         * @param args
         */
        public static void main(String[] args) throws Exception{
                // TODO Auto-generated method stub
                        ArrayList<String> c=new ArrayList<String>();
                        c.add("abc");
                        c.add("wang");
                        //System.out.println(c.get(1));
                       
                        c.getClass().getMethod("add",Object.class).invoke(c,23);;
                       
                       
                        System.out.println(c.get(2));
        }

}
为什么就运行不同过呢,老是出这个java.lang.Integer cannot be cast to java.lang.String;


作者: linjl_ll    时间: 2013-11-14 20:47
本帖最后由 linjl_ll 于 2013-11-14 20:48 编辑

c.getClass().getMethod("add",Object.class).invoke(c,23);;这条语句的执行过程是首先自动装箱
把23装成对应的Integer对象,然后把这个对象放入了集合当中,然后你调用System.out.println(c.get(2));首先get(2)返回23对应的Integer对象,然后因为在println方法中,此时就会把Integer对象转变成String,这个时候出现Integer无法转化成String对象的错误,你可以用这条语句c.get(2) instanceof Integer看下获取的对象是不是Integer。
还有这条语句c.getClass().getMethod("add",Object.class).invoke(c,23)能够执行成功就说明泛型是编译时技术编译之后泛型擦除。不然的话这里只能添加String的对象。

作者: 编程的梦想    时间: 2013-11-15 12:01
linjl_ll 发表于 2013-11-14 20:47
c.getClass().getMethod("add",Object.class).invoke(c,23);;这条语句的执行过程是首先自动装箱
把23装成对 ...

我是看老师的视频写的,再说这个反射的作用就是实现在已知类型中的集合中插入其他类型的数据,你给的那条语句有误啊,编译就过不了
作者: linjl_ll    时间: 2013-11-15 12:25
编程的梦想 发表于 2013-11-15 12:01
我是看老师的视频写的,再说这个反射的作用就是实现在已知类型中的集合中插入其他类型的数据,你给的那条 ...

恩我疏忽了,那个由于泛型的缘故编译器会认为get(2)返回的就是String对象,所以让String对象instanceof Integer是错误的,那可以直接system.out.println(get(2).getClass()).

“再说这个反射的作用就是实现在已知类型中的集合中插入其他类型的数据”,这可不是反射的作用啊!!!

我的意思是假设泛型不是编译时擦除就是在字节码中还有泛型那么,通过反射添加23的时候会进行类型检测,那显然23就不能添加进去了。
作者: 编程的梦想    时间: 2013-11-15 13:24
linjl_ll 发表于 2013-11-15 12:25
恩我疏忽了,那个由于泛型的缘故编译器会认为get(2)返回的就是String对象,所以让String对象instanceof ...

可是老师讲解的视频中就在一个字符串类型的集合中插入了整数类型的元素,运行成功了,
作者: linjl_ll    时间: 2013-11-15 15:05
本帖最后由 linjl_ll 于 2013-11-15 15:17 编辑

恩,我再去看了下老师的视频。老师的代码是:
ArrayList<Integer> al = new ArrayList<Integer>();
al.getClass().getMethod("add", Object.class).invoke(al, "aaaaaa");
System.out.println(al.get(0));
而你的是ArrayList<String>中添加了整数,然后我把你的代码修改了下
ArrayList<String> al = new ArrayList<String>();
al.getClass().getMethod("add", Object.class).invoke(al, 23);
Object mm = al.get(0);
System.out.println(mm);//输出23
System.out.println(al.get(0));//这一行报错

上面的第一个输出语句能正常的输出,而最后一条语句运行报错(Integer转String无法转换错误)。
那就纳闷了这条语句
System.out.println(al.get(0));哪里进行了类型转换??
那我们看看print源码中是怎么定义的呢?这个方法有2个重载函数:
//打印字符串的
public void println(String x) {
    synchronized (this) {
        print(x);
        newLine();
    }
    }

//打印对象的
public void println(Object x) {
        String s = String.valueOf(x);//valueOf函数内的代码为return (obj == null) ? "null" : obj.toString();
        synchronized (this) {
            print(s);
            newLine();
        }
    }
System.out.println(al.get(0));这条语句在编译时由于编译器认为al.get(0)是一个字符串对象,所以编译生成的字节码中调用的是 println(String x)这个方法,所以当运行到这行代码时是把Integer对象传递给了print(String x)这个函数,这就发生了类型转换,就错了。那老师的代码为什么没报错呢?因为老师那条输出语句调用的是public void println(Object x)这个打印函数,传递string对象调用该函数完全是没问题的。
然后我当时说的al.get(2).getClass();运行是错误的也是类型转换错错误,无论是上述那种情况都是错误的,但是我写成这样Object o = al.get(2);system.out.println(o.getClass());就没问题,这个应该是和输出类似的原理把。
当时回复时没有认真的推敲,很抱歉!

作者: 编程的梦想    时间: 2013-11-15 15:46
linjl_ll 发表于 2013-11-15 15:05
恩,我再去看了下老师的视频。老师的代码是:
ArrayList al = new ArrayList();
al.getClass().getMethod(" ...

嘿嘿,其实你已经很认真了,谢谢你了,我再调试调试
作者: 张鹏    时间: 2013-11-15 16:49
原因很简单c.get(2)取得的是String类型,那就说明他发生了类型转换
而泛型是编译器自动添加“强制转换”所以出错。
System.out.println((Object)c.get(2));就没问题了

作者: 张鹏    时间: 2013-11-15 16:52
java中泛型的实现并非正真意义上的泛型,而是编译器自己添加代码实现的,用的还是强制转换。
理解这个题,你可以把生成的class反编译就明白了。
作者: 杨增坤    时间: 2013-11-15 17:18
  1. public static void main(String[] args) throws IllegalArgumentException, SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
  2.                 ArrayList<String> c=new ArrayList<String>();
  3.         c.add("abc");
  4.         c.add("wang");
  5.         //System.out.println(c.get(1));
  6.         
  7.         c.getClass().getMethod("add",Object.class).invoke(c,23);;
  8.         
  9.         System.out.println(c);
  10.         //System.out.println(c.get(2));
  11.         }
复制代码
这样就可以查看集合中的所有元素,get获得与集合类型一样类型的元素。

希望对你有帮助!





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