黑马程序员技术交流社区
标题:
泛型反射问题
[打印本页]
作者:
胡志翔
时间:
2013-9-10 18:03
标题:
泛型反射问题
ArrayList<String> al = new ArrayList<String>();
al.getClass().getMethod("add", Object.class).invoke(al, 3);
System.out.println(al.get(0));
上面的代码会报错: java.lang.Integer cannot be cast to java.lang.String
为什么下面的代码正常?
ArrayList<Integer> al = new ArrayList<Integer>();
al.getClass().getMethod("add", Object.class).invoke(al, "abc");
System.out.println(al.get(0));
作者:
杨彬
时间:
2013-9-10 18:11
本帖最后由 杨彬 于 2013-9-10 18:19 编辑
ArrayList<String> arr = new ArrayList<String>();
Class clazz = arr.getClass();
Method method = clazz.getDeclaredMethod("add", Object.class);
method.invoke(arr, new Integer(1));
method.invoke(arr, new Integer(22));
method.invoke(arr, new String("yb"));
System.out.println(arr);
复制代码
作者:
早知道
时间:
2013-9-10 19:13
这个还真的不懂
不过发现
ArrayList<Integer> al = new ArrayList<Integer>();
al.getClass().getMethod("add", Object.class).invoke(al, "abc");
System.out.println(al.get(0).getClass());
就会报同样的错
作者:
杨彬
时间:
2013-9-10 20:34
***********
ArrayList<String> al = new ArrayList<String>();
al.getClass().getMethod("add", Object.class).invoke(al, 3);
System.out.println(al.get(0));
***********
1. 我现在做一个实验 就是把你的最后的System.out.println(al.get(0));修改为:al.get(0);
这个时候 是不报错的!!这说明这个报错的原因不在泛型,而是println方法出了问题!!
2. 有一个原则:编译时候 如果有重载方法的调用,编译器要确定运行时候的到底调用哪一个重载方法
你现在ArrayList限定为<String> 这样 编译到最后一行的时候,会直接调用public void println(String x);
调用这个重载的原因是:编译的时候,ArrayList<String>的get方法返回的是String类型的数据
【注意 泛型信息是在编译完成之后被擦出的 编译的过程中是非常有用的】
【如果你不信 你就使用ctrl+左键 点击 println这个位置 就会自动跳到 PrintStream类的public void println(String x) 而不是其他的println重载方法】
所以 编译到最后一行的时候 get方法返回String类型的值,调用的就是println(String x)
3. 现在你的第二行传入的是3,一个基本数据类型的值。JDK自动装箱成Integer类型的变量存放在al的集合中。
因为你使用的是反射 所以 现在要考虑运行时候的运行过程:
al.get(0) 编译时 返回String类型的信息,但是编译完成,反省被擦出,get就返回了Object类型的数据。这样 你这里面al.get(0)运行的得到的结果就相当于这句: (Object)new Integer(3);
现在 你把个强制类型转换的对象(Object)new Integer(3)传给了接收参数是String类型的println方法 , JVM就会去执行这个操作。但是他会发现里面的数据真正的类型是Integer,Integer类型的对象没有办法转换成String类型的对象,因此直接抛出 ClassCastException这个类型转换异常
【注意 之所以可以使用强制类型转换 前提就是 要转换的两个对象之间是具有继承关系的对象 否则 只要你敢转换 就就会抛出类型转换异常】
*****************************回答你第二个问题
ArrayList<Integer> a2 = new ArrayList<Integer>();
a2.getClass().getMethod("add", Object.class).invoke(a2, "12");
System.out.println(al.get(0)); -----运行正常
[1] . println的重载方法接收的参数有:boolean、整形(int short)、浮点型、char、Object 和 String
[2]. 按照刚才的说法 这里编译的时候 a2被限定成元素是Integer的类型。这样编译器在编译到最后一行的时候,就会发现你的get方法返回的是Integer类型,这样编译器就把JVM运行的println方法指定到println(Object obj);【同样 使用ctrl+左键 点击 println这个位置 就会自动跳到 PrintStream类的public void println(Object obj) 而不是其他的println重载方法】
[3]. 现在 你在al中通过反射的形式 传入了字符串"12" 这样 JVM运行时【此时泛型已被擦出】,get方法返回的数据相当于(Object)"12"
JVM运行到最后一行的时候,就把这个数据(Object)"12"传入到 println(Object obj)方法中 这个参数传递是没问题的。
此时:println(Object obj)的源码如下:
public void println(Object x) {
String s = String.valueOf(x);
synchronized (this) {
print(s);
newLine();
}
}
这样 相当于 运行了String s = String.valueOf((Object)"12"); 这个运行 就是把Object类型的数据 转换成String类型的数据。这个是没有问题的。之后调用print()方法 打印出来就正确了。
所以 问题的根源就在于:编译的时候,根据编译得到的实际参数【有泛型信息在里面】的类型来决定重载方法的调用 ---- 其一
println(String)和println(Object)同样接收数据 一个传入的是Integer 强转到String 报异常 另一个是String转换到Object 没有问题 。 -----其二
这样 你所叙述的情景就出现了。
欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/)
黑马程序员IT技术论坛 X3.2