ArrayList的get()方法能够直接将Object数组返回成泛型里的类型T,是因为Java的泛型在运行时是被擦除的。这意味着,虽然我们在编写代码时使用了泛型,但在实际运行时,泛型的类型信息是不存在的。所以,尽管ArrayList内部实际上是一个Object数组,但是在你使用ArrayList的get()方法时,Java编译器会确保你获取的对象是你期望的类型。
然而,在你的自定义类中,如果你试图直接返回一个Object数组并期望它自动转换为泛型类型,编译器会报错,因为它不能保证Object数组中的所有元素都是泛型类型T。所以,你需要显式地进行类型转换。
对于你提供的ArrayList源码,elementData(int index)
方法返回的是Object数组中的一个元素,而不是整个数组。因此,编译器可以确保返回的对象是泛型类型E。
如果你希望你的自定义类能够像ArrayList那样工作,你可能需要设计你的类以隐藏内部的实际数据结构,并提供适当的方法来访问和修改数据。例如,你可以提供一个方法来返回特定索引处的元素,而不是直接返回整个数组。这样,你就可以在返回元素之前进行类型转换,从而确保返回的是正确的类型。
例如:
public class MyList<T> {
private Object[] elements;
public T get(int index) {
return (T) elements[index];
}
}
在这个例子中,get()
方法会在返回元素之前将其转换为泛型类型T。如果元素不能被转换为类型T,那么在运行时将会抛出一个ClassCastException
。这是Java类型系统的一部分,它确保了在运行时不会发生类型错误。
你这个代码是不是通过 IDE 里的 go to definition 看到的。
对 ArrayList ,这种方式看到的不是实际代码,而是通过反汇编得到的。
而对于这类 generic 函数,类型检查实际上貌似是在最终不再有 generic 类型的调用处发生的,而不是被调函数里。所以反汇编的代码里就看不到类型转换了。
但是实际代码,没有类型转化是无法通过编译的。
这应该是由于类型擦除,在 elementData 函数体是无法得到 E 具体是什么类型的,也就无法进行实际的转换。实际的转换要一直延迟到可以确定 E 的位置才能发生。
javap -c Generic.class
javap -c Foo.class
可以看到类型检查(转换)的代码是在 Foo.A 里,而不在 Generic.getInteger 里。所以如果是对 Generic.getInteger 进行反汇编,可能就看不到里面的类型转换了。