A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

今天看到了两个帖子询问有关反射中运用泛型时,出现类型匹配异常的问题,研究了一下,希望不对地方大家指正。题目如下:

ArrayList<Integer> al = new ArrayList<Integer>();
          al.getClass().getMethod("add", Object.class).invoke(al, "abc");
          System.out.println(al.get(0));
//结果:abc
ArrayList<String> al = new ArrayList<String>();
          al.getClass().getMethod("add", Object.class).invoke(al, 69);
          System.out.println(al.get(0));
//报错:java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String

为了测试我加入了一些语句,全文如下:
import java.util.*;
class  Test2
{
        public static void main(String[] args) throws Exception
        {
                ArrayList<Integer> al = new ArrayList<Integer>();
                al.getClass().getMethod("add", Object.class).invoke(al,"abc");
                //al.get(0);
                //Integer in = al.get(0);
                //System.out.println(in);
                System.out.println(al.get(0));

                ArrayList<String> al2 = new ArrayList<String>();
                al2.getClass().getMethod("add", Object.class).invoke(al2, new Test1());
                //al2.get(0);
                //String str = al2.get(0);
                //System.out.println(str);
                System.out.println(al2.get(0));

        }
}

class  Test1
{
                public String toString(){
                        return "abcd";
        }
}

首先我在上述两部分语句中单独调用了取出的方法,均未出现异常,然后根据泛型定义了取出值的类型,编译通过。说明编译时期测试的是变量的类型是否匹配。首先看定义为Integer泛型的代码,传入的是String类型的变量仍然能够打印,为了测试,我自己写了一个类,覆盖了toString方法,可打印出结果。说明其打印方法调用了传入对象的toString方法。而在下面定义泛型为String的代码中使用上述测试方法会在运行时抛出异常,说明打印时并未调用对象toString方法。经过分析,得出原理如下:(如不正确请指正)
定义泛型为Integer时,内部元素取出后的类型应为Integer类型,在打印时就需要调用toString方法。
而定义泛型为String时,内部元素取出后类型应为String类型,在打印String类型时,并不调用其toSting方法(参见PrintStream类print(String s)方法)。而是按照平台的默认字符编码将字符串的字符转换为字节。
有参数可知,当定义泛型为Integer时,传入的参数类型是String类型,但根据泛型限定,它被规定为Integer类型,可以理解为带着Integer类型面具的String对象,打印时调用对象toString方法,而且这个对象本身有toString方法,所以能被打印。
定义泛型为String时,内部元素取出后应为String类型,在打印时不需要调用toString方法,而是直接打印,但其对象实际为Integer类型,编译器不调用toString方法就没有办法打印,此时就发生了实际类型与泛型限定的取出类型不匹配异常。

以上为我的理解,期待大家的交流。

评分

参与人数 1技术分 +1 黑马币 +20 收起 理由
职业规划-刘倩老师 + 1 + 20

查看全部评分

6 个回复

倒序浏览
嗯,就是这样的,这位同学分析的很全面,顶一个!
回复 使用道具 举报
分析的很有道理。的确是因为调用其他对象的时候都经过了toString这个方法了。而到String的时候直接调用了Println(String s)造成的问题。看懂了,辛苦了,拜谢一下。
回复 使用道具 举报
lz讲的很细,貌似听明白了~~下午我在看的时候,也改成过张同学的写法:
Object obj=a2.get(0);
System.out.println(obj);
当时虽然运行正常,不过不知道如何理解。是不是这个意思:a2定义的是String类型的泛型,所以他的成员在打印时,不需要调用toString方法,但是加入了一个Integer类型的对象后,不调用toString方法则不能打印,所以先把它取出来赋给一个Object类型的引用,这样,打印时,就可以根据实际obj的类型去判断需不需要调用对象的toString方法?所以运行就没问题了?
回复 使用道具 举报
刘少伟 发表于 2012-4-27 20:38
lz讲的很细,貌似听明白了~~下午我在看的时候,也改成过张同学的写法:
Object obj=a2.get(0);
System.out. ...

恩,我在做实验时也用了你写的这个代码,我感觉是你说的那样,但是我也不能太肯定,上面写的都是我的分析,没有权威验证,呵呵。
回复 使用道具 举报
本帖最后由 李龙276596456 于 2012-8-17 23:14 编辑
施俊 发表于 2012-4-27 21:51
恩,我在做实验时也用了你写的这个代码,我感觉是你说的那样,但是我也不能太肯定,上面写的都是我的分析 ...


前辈,很高兴看到你的这篇帖子,
因为我今天也发了一个同样疑问的帖子,然后有人给我了你的帖子的链接。
但是我做了一个例子,用你的解释却解释不了
3.
public static void main(String[] args) throws IllegalArgumentException, SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
  List<Integer> list = new ArrayList<Integer>();
  list.getClass().getMethod("add", Object.class).invoke(list, "abc");
  System.out.println(list.get(0).toString());
}
仍然出错:
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
at cn.leo.Game.main(Game.java:31)
期待与你交流
我的帖子:http://bbs.itheima.com/forum.php?mod=viewthread&tid=22657&page=1&extra=#pid133087
回复 使用道具 举报
施俊 发表于 2012-4-27 21:51
恩,我在做实验时也用了你写的这个代码,我感觉是你说的那样,但是我也不能太肯定,上面写的都是我的分析 ...

昨天是我没有理解清楚,今天再看的时候,发现你说的很对,感谢
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马