黑马程序员技术交流社区

标题: 泛型:利用反射跳过编译器对泛型检查 [打印本页]

作者: 唐辉辉    时间: 2012-6-28 11:10
标题: 泛型:利用反射跳过编译器对泛型检查
本帖最后由 唐辉辉 于 2012-6-29 13:06 编辑
  1. import java.util.ArrayList;


  2. public class FanXingDemo2 {
  3.         
  4.         public static void main(String[] args) throws Exception{
  5.                
  6.                 ArrayList<String> arrList = new ArrayList<String>();
  7.                
  8.                 arrList.add("123");
  9.                
  10.                 arrList.getClass().getMethod("add", Object.class).invoke(arrList, 123);
  11.                
  12.                 System.out.println(arrList.get(1));
  13.         }
  14. }
复制代码
  1. import java.util.ArrayList;


  2. public class FanXingDemo2 {
  3.         
  4.         public static void main(String[] args) throws Exception{
  5.                
  6.                 ArrayList<Integer> arrList = new ArrayList<Integer>();
  7.                
  8.                 arrList.add(123);
  9.                
  10.                 arrList.getClass().getMethod("add", Object.class).invoke(arrList, "123");
  11.                
  12.                 System.out.println(arrList.get(1));
  13.         }
  14. }
复制代码
为什么第一段代码取出时会报异常,而第二段代码不会:
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
        at FanXingDemo2.main(FanXingDemo2.java:14)

在第一段代码中,已经知道arrList有两个值,一个为String类型,一个为Integer。通过arrList.get()方法 返回,是返回的一个泛型类型的值,也就是我指定的String。 所以会报类型转换异常,但是为什么第二段代码他不报错呢?  明明我指定的是Integer的泛型。利用反射存入的是String类型的值。这时arrList.get()方法返回应该是Integer,为什么这里返回的是String。没有报错。而第一段代码报错了。



作者: 闾丘日月    时间: 2012-6-28 11:15
不是都看到这个异常了么
java.lang.Integer cannot be cast to java.lang.String
我感觉你是为了给大家加分了,我就不仔细说了。
作者: 唐辉辉    时间: 2012-6-28 11:25
本帖最后由 唐辉辉 于 2012-6-28 11:35 编辑
闾丘日月 发表于 2012-6-28 11:15
不是都看到这个异常了么
java.lang.Integer cannot be cast to java.lang.String
我感觉你是为了给大家加分 ...

:L    不对, 我真没搞得懂,  麻烦你跟我说下。    Integer 转 String 不能转, 那为什么String 转 Integer能转呢?
作者: 常佳杰    时间: 2012-6-28 12:16
这样才是对的..
  1. public class Test7 {
  2.                 public static void main(String[] args) throws Exception{
  3.                         
  4.                         ArrayList<String> arrList = new ArrayList<String>();
  5.                         
  6.                         arrList.add("123");
  7.                         
  8.                         arrList.getClass().getMethod("add", Object.class).invoke(arrList, "123");
  9.                         
  10.                         System.out.println(arrList.get(1));
  11.                 }
  12.         }
复制代码
add(int index, E element)   将指定的元素插入此列表中的指定位置...
第二个参数必须是字符型... 你传进的是个整型肯定会报错...

作者: 唐辉辉    时间: 2012-6-28 13:12
本帖最后由 唐辉辉 于 2012-6-28 13:15 编辑
常佳杰 发表于 2012-6-28 12:16
这样才是对的..add(int index, E element)   将指定的元素插入此列表中的指定位置...
第二个参数必须是字符 ...

add(int index, E element)
第二个参数并没有指定是某种类型,   这里明明是一个泛型。虽然我在前面指定了这个泛型为String ,但是在 这个类的字节码中已经去除了这个泛型的类型。所以,我放任何类型的元素都应该是可以的。 并且报错的是 14 行。
作者: 常佳杰    时间: 2012-6-28 13:22
哦 写错了 应该是
invoke(arrList, "123");
这个的第二个...
这样就有结果了
作者: 唐辉辉    时间: 2012-6-28 13:29
常佳杰 发表于 2012-6-28 13:22
哦 写错了 应该是
invoke(arrList, "123");
这个的第二个...

我知道这样是正确的,   麻烦你讲下原因。是哪里出的问题,为什么?
作者: 李天甲    时间: 2012-6-28 13:48
同上面说的
java.lang.Integer cannot be cast to java.lang.String
下面这样写就可以呵呵
import java.util.ArrayList;
class FanXingDemo2 {
        public static void main(String[] args) throws Exception {
                ArrayList<? super String> arrList = new ArrayList();
                arrList.add("123");
                arrList.getClass().getMethod("add", Object.class).invoke(arrList, 123);
                System.out.println(arrList.get(1));
        }
}
作者: 唐辉辉    时间: 2012-6-28 14:13
李天甲 发表于 2012-6-28 13:48
同上面说的
java.lang.Integer cannot be cast to java.lang.String
下面这样写就可以呵呵

说说为什么啊?  我写的为什么错了?

还有, 为什么你指定<?  super  String>可以呢?
作者: 胡文杰    时间: 2012-6-28 14:16
楼上说的不错,但是编译是会有注意,而且不能解决LZ的第二种代码

方法里面这样写就能解决楼主的问题了!

ArrayList<Object> arrList = new ArrayList<Object>();
                                
                arrList.add("123");
                                
                arrList.getClass().getMethod("add", Object.class).invoke(arrList, 12);
               
                for(Object obj : arrList)
                {
                        System.out.println(obj);
                }
作者: 常佳杰    时间: 2012-6-28 14:29
public class Test7 {
                public static void main(String[] args) throws Exception{
                        
                        ArrayList<String> arrList = new ArrayList<String>();
                        
                        arrList.add("123");
                        
                        arrList.getClass().getMethod("add", Object.class).invoke(arrList, 123);
                        
                        System.out.println(arrList.get(0));
                }
        }
这样不会出问题,输出:
123
我的理解是这样的..
arrList.getClass().getMethod("add", Object.class).invoke(arrList, 123);这句话虽然把123添加进去了..
存进去的123共占12个字节,存到数组中它是字符型...
但是当你用arrList.get(1)..得这个数组中的索引值为1的时候..编译器从中取1个字节时出错,
取不出来..
但是迭代一下就能输出0,1索引处的值

public class FanXingDemo2 {
                        ArrayList<String> arrList1 = new ArrayList<String>();
                        
                        arrList1.add("123");
                        
                        arrList1.getClass().getMethod("add", Object.class).invoke(arrList,123);
                       
                        System.out.println(arrList1.get(0));
                        
                        Iterator it = arrList1.iterator();
                        while(it.hasNext()){
                                System.out.println(it.next());
                        }

输出:
123
123

作者: 常佳杰    时间: 2012-6-28 14:31
楼主看看,这个弄了好长时间,可能就是这个原因..
作者: 李天甲    时间: 2012-6-28 14:34
呵呵,的确是System.out.println(arrList.get(1));
这一句搞得鬼
调试了一下,完全可以存进去,但是输出的时候因为有了泛型的限定,所以根本输出不出来,记得哪里回答过...
10楼正解.....
作者: 唐辉辉    时间: 2012-6-28 15:04
常佳杰 发表于 2012-6-28 14:29
public class Test7 {
                public static void main(String[] args) throws Exception{
       ...

感谢你的回答:

你写的这个已经避开了问题。it.next()输出的是一个对象,所以任意类型的对象都可以输出,是没错的。 我的问题是在第一段代码中,已经知道arrList有两个值,一个为String类型,一个为Integer。通过arrList.get()方法 返回,是返回的一个泛型类型的值,也就是我指定的String。 所以会报类型转换异常,但是为什么第二段代码他不报错呢? 明明我指定的是Integer的泛型。利用反射存入的是String类型的值。这时arrList.get()方法返回应该是Integer,为什么这里返回的是String。没有报错。而第一段代码报错了。
作者: 唐辉辉    时间: 2012-6-28 15:04
李天甲 发表于 2012-6-28 14:34
呵呵,的确是System.out.println(arrList.get(1));
这一句搞得鬼
调试了一下,完全可以存进去,但是输出的时候 ...

第二段代码也有泛型限定。 为什么没有报错,正确的输出了呢?
作者: 唐辉辉    时间: 2012-6-28 15:05
胡文杰 发表于 2012-6-28 14:16
楼上说的不错,但是编译是会有注意,而且不能解决LZ的第二种代码

方法里面这样写就能解决楼主的问题了!

感谢!

问题是:

在第一段代码中,已经知道arrList有两个值,一个为String类型,一个为Integer。通过arrList.get()方法 返回,是返回的一个泛型类型的值,也就是我指定的String。 所以会报类型转换异常,但是为什么第二段代码他不报错呢?  明明我指定的是Integer的泛型。利用反射存入的是String类型的值。这时arrList.get()方法返回应该是Integer,为什么这里返回的是String。没有报错。而第一段代码报错了。
作者: 周朋飞    时间: 2012-6-29 03:09
第一个出错的原因是,arrlist运行的时候去类型化,存到i里面的都是Object类型的,你可以debug看一下,当你去取的时候你本身定义为Integer却取到一个String ,有没有调用什么方法进行转换 所以会出错
第二个不会出错就是下面,他会先给你转换一下,再输出

println
public void println(Object x)
Prints an Object and then terminate the line. This method calls at first String.valueOf(x) to get the printed object's string value, then behaves as though it invokes print(String) and then println().

作者: 周朋飞    时间: 2012-6-29 03:09
第一个出错的原因是,arrlist运行的时候去类型化,存到i里面的都是Object类型的,你可以debug看一下,当你去取的时候你本身定义为Integer却取到一个String ,有没有调用什么方法进行转换 所以会出错
第二个不会出错就是下面,他会先给你转换一下,再输出

println
public void println(Object x)
Prints an Object and then terminate the line. This method calls at first String.valueOf(x) to get the printed object's string value, then behaves as though it invokes print(String) and then println().





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