论坛首页 Java企业应用论坛

泛型理解上的一个问题

浏览 10379 次
该帖已经被评为隐藏帖
作者 正文
   发表时间:2010-11-07   最后修改:2010-11-07
OO
请帮忙解释下这个泛型问题,具体逻辑我都下在代码注释里面了
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));
	}

}

   发表时间: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进行了优化??
0 请登录后投票
   发表时间: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型的对象,就有问题了,报转换异常.....
0 请登录后投票
   发表时间:2010-11-07  
你可以定义其它number类型的加入String 都没有问题。可能原因就是string属于引用类型,内存地址保存到int或long类型数组就没问题,反过来自然不行
1 请登录后投票
   发表时间:2010-11-07  
wangzh1118 写道
你可以定义其它number类型的加入String 都没有问题。可能原因就是string属于引用类型,内存地址保存到int或long类型数组就没问题,反过来自然不行


但是不管泛型指定为何种类型,他们加载到内存中的时候都是一种类型,我的帖子中arr1.getClass()==arr2.getClass()测试了这个,所以在通过编译器后他们在内存中操作都是当做一个对象来对待的,number下面的类型也会自动装箱.
0 请登录后投票
   发表时间: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会抛出异常可能是编译器考虑到其它的一些原因而没有进行优化。

当然,这仅仅是我的猜想,仅供参考
0 请登录后投票
   发表时间:2010-11-08   最后修改:2010-11-08
你们的瞎猜真没创意

改成 ArrayList arr2=new ArrayList<String>();  

就不会有异常
至于为什么 回去慢慢想
0 请登录后投票
   发表时间:2010-11-08  
qianhd 写道
你们的瞎猜真没创意

改成 ArrayList arr2=new ArrayList<String>();  

就不会有异常
至于为什么 回去慢慢想



那为什么arr1没问题?
0 请登录后投票
   发表时间:2010-11-08  
你假如的是基本类型 而不是封装类
0 请登录后投票
   发表时间: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));
    }
}

这个是你的代码反编译以后的代码。。。然后你的注释,就都得到解答了。。。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics