这个程序为什么没有出错

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]

并没有看到上面提到的[color=red][size=medium]checkcast[/size][/color]汇编语句。所以当打印出intList.get(0)时并没有强制转化为Integer类型。

至于它输出字符串“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]

这个问题的确的提的很好, :idea:

接下来分析下:
先仔细读读,体会这句话,读上两三篇:
[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