public class Test {
public static void append(List list){
list.add("asdf");
}
public static void main(String[] args) {
List<Integer> intList = new ArrayList<Integer>();
append(intList);
System.out.println(intList.get(0));
}
}
我们先来看看java编译器是怎么解析如下这句代码的。
[code="java"]intList.get(0).getClass();[/code]
java反汇编一下,生成如下对应的java汇编语言:
[img]http://dl.iteye.com/upload/picture/pic/58197/914360ca-cfd4-3348-a814-914c0c69ab90.jpg[/img]
从第14,我们可以看出get真正返回的类型是Object类型。
注意了,在第14的get方法之后第19是[color=red][size=medium]checkcast[/size][/color]语句,紧跟着后面的是转化为Integer类型。
好了,“String类型-->Object类型--->Integer类型”这个过程肯定报错。这下子明白了错误就出在这里。
回过头去看看前面。
然后我用同样的方法去看看这面这句为什么没错:
[code="java"]System.out.println(intList.get(0)); [/code]
从上面,我们知道get返回的数据类型是Object。
所以我写出如下代码:
[code="java"]Object i = intList.get(0);
i.getClass();[/code]
反汇编成java汇编语言:(见下面)
[img]http://dl.iteye.com/upload/picture/pic/58201/c586fb0a-e018-31b6-a214-da981d4a6073.jpg[/img]
至于它输出字符串“sdf” ,那是多态的机制。
[b]看ArrayList的 源码, 可以发现ArrayList中还是 Object的数组,来容纳加入的元素的。[/b]
[code="java"]
public class ArrayList extends AbstractList
implements List, RandomAccess, Cloneable, java.io.Serializable
{
private static final long serialVersionUID = 8683452581122892189L;
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer.
*/
private transient Object[] elementData;[/code]
问题在于方法append(List list)参数的定义,这里的参数实际相当于append list>,而调用System.out.println(intList.get(0)); 相当于System.out.println((Object)intList.get(0).toString()); 所以不会出错。
换句话说,虽然定义了Integer,但实际操作时只是针对了Object对象,把Object放到list中,而后又拿出来这个Object,并没有涉及到类型转换的操作,正因为这些操作针对的对象都是Object,而显然Integer也属于Object,所以才不会出错。
那是因为问题出在这个方法的参数上。
现在分析一下这个方法
[code="java"]public static void append(List list){
list.add("asdf");
} [/code]
首先我们知道java中方法的传参数都是传值(可能你想到参数是对象,这种情况下传对象引用(地址))。
其次,这个方法中传的是list对象,所以传的是list对象的引用(地址)。
再次,引用(地址)只含有对象在堆中的地址信息,并没有包括泛型等相关信息。
再次,再传对象引用给方法时,是这样的一个过程,把对象的引用cpoy一份给方法的参数。所以方法中的参数list和方法外的list(ArrayList)是不一样的。
最后,由于方法参数声明的是List,并不是List,所以它对其中的元素具体是什么类型并没有限制。
如果是写法如下:
[code="java"]public static void append(List list){
list.add("asdf");
} [/code]
问题还是出现在方法传参上面。
关键你要理解泛型在java编译和运行是怎么实现的
泛型在编译时都会被擦除成Object类型,所以在编译期间不会出错
至少运行时为什么也不出错,那就要看ArrayList的底层实现机制了
分析源码可以发现
add方法
public boolean add(E e) {
ensureCapacity(size + 1); // Increments modCount!!
elementData[size++] = e;//新加入的对象保存在elementData数组中
return true;
}
我们再来看一下elementData数组是怎么定义的
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer.
*/
private transient Object[] elementData;//Object数组
[quote]那当intList用get方法取值时,会把取得的Object的值转回Integer类型,这个时候应该会报异常啊,但是没有。
[/quote]
接下来分析下:
先仔细读读,体会这句话,读上两三篇:
[quote]
Java 语言中的泛型基本上完全在编译器中实现,由编译器执行类型检查和类型推断,然后生成普通的非泛型的字节码。这种实现技术称为擦除(erasure)(编译器使用泛型类型信息保证类型安全,然后在生成字节码之前将其清除)
[/quote]
1,确定在编译的时候没有错误,编译器没有检测到类型错误。
2,然后编译器生成普通的非泛型的字节码,相当于无泛型的java代码编译成二进制代码。我们来看看你这段代码用无泛型的java代码怎么写:如下
[code="java"]public class Test {
public static void append(List list){
list.add("asdf");
}
public static void main(String[] args) {
List intList = new ArrayList();
append(intList);
System.out.println(intList.get(0));
}
}
[/code]看出来了吧,肯定不会报错。
再来看看你的第二个问题:
[quote]
如果打印的是intList.get(0).getClass()的话就会报java.lang.String cannot be cast to java.lang.Integer的java.lang.ClassCastException。[/quote]
接下...
最后,
贴上参考过的资料:
http://www.ibm.com/developerworks/cn/java/j-jtp01255.html
http://www.blogjava.net/calvin/archive/2006/04/28/43830.html
http://www.blogjava.net/iamtin/archive/2006/05/08/45091.html
http://dev.csdn.net/author/hivon/92d0dd0c711d4cf3b00354776e0aac02.html