ArrayList的get()方法为什么可以直接将Object数组返回成泛型里的类型?

ArrayList的get()方法为什么可以直接将Object数组返回成泛型里的T?
今天阅读ArrayList源码时,为什么可以直接在返回类型为泛型的方法里直接返回Object数组,而我自己定义的类里却需要强转?
以下是ArrayList源码

public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable {
    transient Object[] elementData;

    E elementData(int index) {
        return this.elementData[index];
    }
}

而我的代码却无法编译

image.png

阅读 1.4k
avatarAI BotBETA

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类型系统的一部分,它确保了在运行时不会发生类型错误。

4 个回答

你这个代码是不是通过 IDE 里的 go to definition 看到的。

对 ArrayList ,这种方式看到的不是实际代码,而是通过反汇编得到的。

而对于这类 generic 函数,类型检查实际上貌似是在最终不再有 generic 类型的调用处发生的,而不是被调函数里。所以反汇编的代码里就看不到类型转换了。

但是实际代码,没有类型转化是无法通过编译的。

这应该是由于类型擦除,在 elementData 函数体是无法得到 E 具体是什么类型的,也就无法进行实际的转换。实际的转换要一直延迟到可以确定 E 的位置才能发生。


package foo;

public class Generic<T> {
    public T getObjectFoo() {
        return (T) new Object();
    }
}

javap -c Generic.class

Compiled from "Generic.java"
public class foo.Generic<T> {
  public foo.Generic();
    Code:
       0: aload_0
       1: invokespecial #8                  // Method java/lang/Object."<init>":()V
       4: return

  public T getObjectFoo();
    Code:
       0: new           #3                  // class java/lang/Object
       3: dup
       4: invokespecial #8                  // Method java/lang/Object."<init>":()V
       7: areturn
}
package foo;

public class Foo {
    public void A() {
        final Generic<Integer> g = new Generic<>();
        final Integer i = g.getObjectFoo();
    }
}

javap -c Foo.class

Compiled from "Foo.java"
public class foo.Foo {
  public foo.Foo();
    Code:
       0: aload_0
       1: invokespecial #8                  // Method java/lang/Object."<init>":()V
       4: return

  public void A();
    Code:
       0: new           #15                 // class foo/Generic
       3: dup
       4: invokespecial #17                 // Method foo/Generic."<init>":()V
       7: astore_1
       8: aload_1
       9: invokevirtual #18                 // Method foo/Generic.getObjectFoo:()Ljava/lang/Object;
      12: checkcast     #22                 // class java/lang/Integer  !!!! 实际的类型检查(转换)
      15: astore_2
      16: return
}

可以看到类型检查(转换)的代码是在 Foo.A 里,而不在 Generic.getInteger 里。所以如果是对 Generic.getInteger 进行反汇编,可能就看不到里面的类型转换了。

是不是你的源码有问题,我这边是有进行强转的

image.png

同楼上,是有强转的
image.png

新手上路,请多包涵

image.png
楼主是哪个版本的jdk,我的里面有强转,jdk版本是1.8.0_361

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题