集合转换成数组时类型向下转型的问题

今天在使用集合转数组的过程中,发现了一个向下类型转换的问题,其实是对基础知识掌握的不牢靠
ArrayList中有两个方法:都可以将List转换为数组形式
Object[] toArray() 
<T> T[] toArray(T[] contents)
其中:
第一种
@Override public Object[] toArray() {
        int s = size;
        Object[] result = new Object[s];
        System.arraycopy(array, 0, result, 0, s);
        return result;
    }
把数组转换为Object对象数组

第二种
@Override public <T> T[] toArray(T[] contents) {
        int s = size;
        if (contents.length < s) {
            @SuppressWarnings("unchecked") T[] newArray
                = (T[]) Array.newInstance(contents.getClass().getComponentType(), s);
            contents = newArray;
        }
        System.arraycopy(this.array, 0, contents, 0, s);
        if (contents.length > s) {
            contents[s] = null;
        }
        return contents;
    }
转换为泛型数组,即我们传进来什么数组,转换后就成为什么数组。

其实两个方法内部都使用了System.arraycopy来进行数组拷贝,不同之处就是在数组对象类型上。


假如我们有一个ArrayList<String> list 集合,需要将这个list转换为String数组
这样写是有问题的:
String[] array= (String[]) list.toArray();
这里就有一个对象向下转型的知识点。
由于list.toArray得到的是一个Object[]数组类型,由Object对象强制转化为String对象会在运行时报类型转换的错误(虽然编译期间是通过的)
01-02 03:49:56.776: E/AndroidRuntime(7438): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.txt.mydemo/com.txt.mydemo.MainActivity}: java.lang.ClassCastException: com.txt.mydemo.MainActivity$Father cannot be cast to com.txt.mydemo.MainActivity$Son
01-02 03:49:56.776: E/AndroidRuntime(7438): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2450)
01-02 03:49:56.776: E/AndroidRuntime(7438): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2510)
01-02 03:49:56.776: E/AndroidRuntime(7438): at android.app.ActivityThread.-wrap11(ActivityThread.java)
01-02 03:49:56.776: E/AndroidRuntime(7438): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1357)
01-02 03:49:56.776: E/AndroidRuntime(7438): at android.os.Handler.dispatchMessage(Handler.java:102)
01-02 03:49:56.776: E/AndroidRuntime(7438): at android.os.Looper.loop(Looper.java:148)
01-02 03:49:56.776: E/AndroidRuntime(7438): at android.app.ActivityThread.main(ActivityThread.java:5507)
01-02 03:49:56.776: E/AndroidRuntime(7438): at java.lang.reflect.Method.invoke(Native Method)
01-02 03:49:56.776: E/AndroidRuntime(7438): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:780)
01-02 03:49:56.776: E/AndroidRuntime(7438): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:670)
01-02 03:49:56.776: E/AndroidRuntime(7438): Caused by: java.lang.ClassCastException: com.txt.mydemo.MainActivity$Father cannot be cast to com.txt.mydemo.MainActivity$Son
01-02 03:49:56.776: E/AndroidRuntime(7438): at com.txt.mydemo.MainActivity.testDownObject(MainActivity.java:106)
01-02 03:49:56.776: E/AndroidRuntime(7438): at com.txt.mydemo.MainActivity.test(MainActivity.java:80)
01-02 03:49:56.776: E/AndroidRuntime(7438): at com.txt.mydemo.MainActivity.onCreate(MainActivity.java:37)
01-02 03:49:56.776: E/AndroidRuntime(7438): at android.app.Activity.performCreate(Activity.java:6251)
01-02 03:49:56.776: E/AndroidRuntime(7438): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1110)
01-02 03:49:56.776: E/AndroidRuntime(7438): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2403)
01-02 03:49:56.776: E/AndroidRuntime(7438): ... 9 more


看下面几个例子:
/**
* 子类比父类功能更强大
     * 子类的方法和属性父类未必有,但是父类的(非私有)属性和方法(父类的私有属性和方法、或是不在同一包内的没有修饰词的属性除外),子类一定有
     */
    private void test(){
// 向上转型都没问题,只是子类的功能被削弱了
        testUpObject();
        testUpArray();
// 向下转型成功,因为实例化的是子类对象
        testDownArray_();
        testDownObject_();
        // 向下转型失败,因为实例化的是父类对象
        testDownObject();
        testDownArray();
    }
    
    private void testUpArray(){
        System.out.println("testUpArray");
        Son[] s = new Son[5];
        Father[] f = (Father[])s;
    }
    
    private void testUpObject(){
        System.out.println("testUpObject");
        Son s = new Son();
        Father f = (Father)s;// 向上转型,为了能使用父类的私有属性
        System.out.println("f.adge: " + f.age);
    }
    
    private void testDownArray(){
        System.out.println("testDownArray");
        Father[] f = new Father[5];// 实例化的是父类元素
        Son[] s = (Son[]) f; // 父类元素一定没有子类元素自己定义的属性和方法,肯定不能转换成功
        
    }
    
    private void testDownObject(){
        System.out.println("testDownObject");
        Father f = new Father();// 实例化的是父类对象
        Son s = (Son)f;// 父类对象一定没有子类自己定义的属性和方法,肯定不能转换成功
    }
    
    private void testDownObject_(){
        System.out.println("testDownObject_");
        Father f = new Son();//注意这里实例化的数组对象是Son类型
        Son s = (Son)f; // 这里f的私有属性age,在转换为Son类型时就失效了
    }
    
    private void testDownArray_(){
        System.out.println("testDownArray_");
        Father[] f = new Son[5];//注意这里实例化的数组对象是Son[]类型
        Son[] s = (Son[]) f;
    }
    
    public class Father{
        private int age = 50;
        public Father() {
            System.out.println("Father");
        }
    }


    class Son extends Father{
        public Son() {
            System.out.println("Son");
        }
    }

由此看出,向下转型是有条件限制的。

所以在第二种方法中,由于使用了泛型,我们在转换数组的时候创建了一个新的准确类型的数组来存储转换后的数据,如:
String[] array = list.toArray(new String[list.size()]);创建了一个列表长度的数组进去,也可以传进一个长度为0的 new String[0]的,toArray函数会自动处理的
toArray内部通过反射已经确定好了数组类型了。

参考资料:
http://bbs.csdn.net/topics/380120664
http://www.cnblogs.com/ihou/archive/2012/05/10/2494578.html
http://blog.csdn.net/waterwindsxu/article/details/20387893
http://bbs.csdn.net/topics/390028191
强制类型转换
http://www.cnblogs.com/chenssy/p/3393160.html

另外插入一个问题:
Object[] obj = {"t1","t2"};  // 实际上实例化的是一个Object数组,数组对象的类型和数组里面存储的数据的类型没有关联性
String[] str1   = (String[])obj;  // 不可行,有异常

用Object[] obj = new String{"t1","t2"};替换是可以运行。 这个是上面向下转型的第一种例子
String[] str1   = (String[])obj;

参考:
http://www.iteye.com/problems/21532
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值