今天看到了两个帖子询问有关反射中运用泛型时,出现类型匹配异常的问题,研究了一下,希望不对地方大家指正。题目如下:
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方法就没有办法打印,此时就发生了实际类型与泛型限定的取出类型不匹配异常。
以上为我的理解,期待大家的交流。
|