锁定老帖子 主题:泛型理解上的一个问题
该帖已经被评为隐藏帖
|
|
---|---|
作者 | 正文 |
发表时间:2010-11-07
最后修改:2010-11-07
public class Test01 { public static void main(String[] args) throws Exception{ ArrayList<Integer> arr1=new ArrayList<Integer>(); ArrayList<String> arr2=new ArrayList<String>(); //下面打印出来true,证明泛型只是编译器级别的一个东西,加载到内存还是一样的 System.out.println(arr1.getClass()==arr2.getClass()); //那么可以用跳过编译器用反射直接加入不通类型的东西,测试打印出"dodo"字符串 arr1.add(55); arr1.getClass().getMethod("add", Object.class).invoke(arr1, "dodo"); System.out.println(arr1.get(1)); //既然如此那么下面这个也应该能正确打印,但是报异常异常为: java.lang.ClassCastException arr2.add("why"); arr2.getClass().getMethod("add", Object.class).invoke(arr2, 33); System.out.println(arr2.get(1)); } } 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2010-11-07
最后修改:2010-11-07
我试了一下,自定义的类型也是可以的:
public class OO { @Override public String toString() { return super.toString(); } public static void main(String[]args) throws IllegalArgumentException, SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { ArrayList<OO> arr3 = new ArrayList<OO>(); arr3.add(new OO()); //arr3.getClass().getMethod("add", Object.class).invoke(arr3, "dodo"); //correct //arr3.getClass().getMethod("add", Object.class).invoke(arr3, 33); //correct arr3.getClass().getMethod("add", Object.class).invoke(arr3, new OO()); //correct System.out.println(arr3.get(1)); } } 我猜:有可能是编译器对String类型的ArrayList进行了优化?? |
|
返回顶楼 | |
发表时间:2010-11-07
francis.xjl 写道 我试了一下,自定义的类型也是可以的:
public class OO { @Override public String toString() { return super.toString(); } public static void main(String[]args) throws IllegalArgumentException, SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { ArrayList<OO> arr3 = new ArrayList<OO>(); arr3.add(new OO()); //arr3.getClass().getMethod("add", Object.class).invoke(arr3, "dodo"); //correct //arr3.getClass().getMethod("add", Object.class).invoke(arr3, 33); //correct arr3.getClass().getMethod("add", Object.class).invoke(arr3, new OO()); //correct System.out.println(arr3.get(1)); } } 我猜:有可能是编译器对String类型的ArrayList进行了优化?? 不是这个问题,第一个元素和第二个元素加的都是一样的,我的意思是可以通过反射这种方式加入另外一种类型,比如你定义的泛型为OO,那么arr3.getClass().getMethod("add", Object.class).invoke(arr3, "why"); 加入String类型的一个对象. 你取值时候会不会报异常呢? 我的代码是说:定义int型的泛型容器通过反射加入String类型的对象,然后get它,没问题 但是如果定义String型的泛型,通过反射加入int型的对象,就有问题了,报转换异常..... |
|
返回顶楼 | |
发表时间:2010-11-07
你可以定义其它number类型的加入String 都没有问题。可能原因就是string属于引用类型,内存地址保存到int或long类型数组就没问题,反过来自然不行
|
|
返回顶楼 | |
发表时间:2010-11-07
wangzh1118 写道 你可以定义其它number类型的加入String 都没有问题。可能原因就是string属于引用类型,内存地址保存到int或long类型数组就没问题,反过来自然不行
但是不管泛型指定为何种类型,他们加载到内存中的时候都是一种类型,我的帖子中arr1.getClass()==arr2.getClass()测试了这个,所以在通过编译器后他们在内存中操作都是当做一个对象来对待的,number下面的类型也会自动装箱. |
|
返回顶楼 | |
发表时间:2010-11-08
最后修改:2010-11-08
下面是我的猜想,仅供参考
再看了一下,我觉得应该是编译器进行了优化,跳过了一些步骤。 我们来读ArrayList的源码,它的add跟get方法是这样的: public boolean add(E e) { ensureCapacity(size + 1); // Increments modCount!! elementData[size++] = e; return true; } public E get(int index) { RangeCheck(index); return (E) elementData[index]; } 因此,你如果在ArrayList<String>运行时插入一个Integer类型的数据,由于范型只存在编译期的缘故,而且add方法没有对类型进行检查,因此,可以成功。但是,当你想取出这个对象时,就有一个问题:get方法中有一个强制转换,按照道理是会抛出异常的,如果在你原来的程序中加入这么一句试一下就知道了: // add by francis.xjl System.out.println(arr1.get(1).getClass()); 因此,其实抛出异常是正常的。 那为什么直接输出arr1.get(1)就可以呢?我估计是这样的:我们知道打印一个对象其实就是调用这个对象的toString()方法,而toString()方法是Object类型中的,因此,可能编译器觉得这里的强制转换没有必要,因此给省略了这个步骤。 因此,我觉得String类型的ArrayList会抛出异常可能是编译器考虑到其它的一些原因而没有进行优化。 当然,这仅仅是我的猜想,仅供参考 |
|
返回顶楼 | |
发表时间:2010-11-08
最后修改:2010-11-08
你们的瞎猜真没创意
改成 ArrayList arr2=new ArrayList<String>(); 就不会有异常 至于为什么 回去慢慢想 |
|
返回顶楼 | |
发表时间:2010-11-08
qianhd 写道 你们的瞎猜真没创意
改成 ArrayList arr2=new ArrayList<String>(); 就不会有异常 至于为什么 回去慢慢想 那为什么arr1没问题? |
|
返回顶楼 | |
发表时间:2010-11-08
你假如的是基本类型 而不是封装类
|
|
返回顶楼 | |
发表时间:2010-11-08
悲剧了 写道 请帮忙解释下这个泛型问题,具体逻辑我都下在代码注释里面了
public class Test01 { public static void main(String[] args) throws Exception{ ArrayList<Integer> arr1=new ArrayList<Integer>(); ArrayList<String> arr2=new ArrayList<String>(); //下面打印出来true,证明泛型只是编译器级别的一个东西,加载到内存还是一样的 System.out.println(arr1.getClass()==arr2.getClass()); //那么可以用跳过编译器用反射直接加入不通类型的东西,测试打印出"dodo"字符串 arr1.add(55); arr1.getClass().getMethod("add", Object.class).invoke(arr1, "dodo"); System.out.println(arr1.get(1)); //既然如此那么下面这个也应该能正确打印,但是报异常异常为: java.lang.ClassCastException arr2.add("why"); arr2.getClass().getMethod("add", Object.class).invoke(arr2, 33); System.out.println(arr2.get(1)); } } // Decompiled by DJ v3.6.6.79 Copyright 2004 Atanas Neshkov Date: 2010-11-08 9:27:39 // Home Page : http://members.fortunecity.com/neshkov/dj.html - Check often for new version! // Decompiler options: packimports(3) // Source File Name: Test.java package com.paic; import java.io.PrintStream; import java.lang.reflect.Method; import java.util.ArrayList; public class Test { public Test() { } public static void main(String args[]) throws Exception { ArrayList arr1 = new ArrayList(); ArrayList arr2 = new ArrayList(); System.out.println(arr1.getClass() == arr2.getClass()); arr1.add(Integer.valueOf(55)); arr1.getClass().getMethod("add", new Class[] { java/lang/Object }).invoke(arr1, new Object[] { "dodo" }); System.out.println(arr1.get(1)); arr2.add("why"); arr2.getClass().getMethod("add", new Class[] { java/lang/Object }).invoke(arr2, new Object[] { Integer.valueOf(33) }); System.out.println((String)arr2.get(1)); } } 这个是你的代码反编译以后的代码。。。然后你的注释,就都得到解答了。。。 |
|
返回顶楼 | |