当前位置: 首页 > 知识库问答 >
问题:

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

艾修然
2024-04-23

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

共有4个答案

公西季
2024-04-23

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

亢雅懿
2024-04-23

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

image.png

弘和同
2024-04-23

你这个代码是不是通过 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 进行反汇编,可能就看不到里面的类型转换了。

陶炫明
2024-04-23

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

 类似资料:
  • 问题内容: 为什么以下代码会编译?该方法返回该类型或其子类的实例。类中的代码调用该方法。编译器允许将返回值存储到类型的变量(显然不在的层次结构中)。 即使在擦除类型之后, 返回类型也不应该仍然是的实例 吗? 该方法的字节码为: 编辑:一致地替换为。 问题答案: 这实际上是合法的类型推断*。 我们可以将其简化为以下示例(Ideone): 因为是接口,所以允许编译器推断(无意义的,实际上是)交集类型。

  • 下面对getHighest()和getLowest()的调用返回Comparable类型的对象,而不是T类型的对象,这正是我们想要的。为什么,我该如何改进这段代码,使这些调用返回T(这样T的字段和方法就可用了)? 下一行生成编译器错误: 错误:找不到符号符号:方法getName()位置:接口java.lang.Comparable 我想employee.getHighest()返回一个员工(而不仅

  • 问题内容: 背景 我曾经写过这种方法: 应该这样称呼它: 这很好用(尽管我在研究当前容易出错的问题时在这里的答案中已经看到)。 目前的情况 无论如何,现在我正在编写以下代码(在扩展javax.servlet.jsp.tagext.TagSupport的类中): 目的是可以这样称呼: 我的评估方法中的代码显然不起作用。的第二个参数应该是Class对象。这导致我: 我的问题 如何获得通用(返回)类型的

  • 我正在实现我自己的泛型链表类,它有一个名为的实例方法,该方法生成链表的数组副本并返回它。然而,每当我试图在实例上调用该方法时,我总是得到错误消息“sllist.this cannot be referenced from a static context”。我搜索了一下,有些人说这是因为我没有在实例上调用那个方法,但我确实调用了。 下面是类: 它有一些方法,如,它们都没有问题。 是我不断收到错误消

  • Java中是否有一种方法可以通过一个方法的声明返回不同的类型? 我希望此方法返回一个对象,并在函数调用时将其转换为正确的类型。这就是我的想法,但它不是这样工作的。我是否需要某种通用返回类型来执行此操作?解决这个问题的最佳方法是什么?