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

数组如何在Java中“记住”它们的类型?

庄嘉
2023-03-14

考虑以下代码:

class AA { }

class BB extends AA { }

public class Testing {

    public static void main(String[] args) {
        BB[] arr = new BB[10];
        AA[] arr2 = arr;

        BB b = new BB();
        AA a = new AA();
        arr2[0] = a; // ArrayStoreException at runtime
        arr2[1] = b;

        List<BB> listBB = new ArrayList<>();
        List listAA = listBB;
        listAA.add("hello world.txt");

    }
}

在上面的例子中,当我尝试arr2[0]=a时,我得到了ArrayStoreException。这意味着数组会记住它必须接受的类型。但是列表不记得它们。它只是简单地编译并运行良好。当我检索对象BB时,将抛出ClassCastException。

所以问题是:

>

  • 数组如何记住它的类型(我知道它被称为“具体化”)。这到底是怎么发生的?

    为什么只有数组被赋予这种能力,而不是ArrayList,尽管它在引擎罩下使用数组。

    为什么不能在编译时检测到ArrayStoreException,即当我检测到arr2[0]=a时,它可能会导致编译器错误,而不是在运行时检测到它。

    谢谢。

  • 共有3个答案

    傅鸿波
    2023-03-14

    数组确实显式地记住它们的类型,您甚至可以在运行时获得它(array.getClass(). getComponentType())。

    在存储到阵列时,VM将检查要存储的元素是否与阵列的组件类型兼容。如果不是,则会得到ArrayStoreException。

    集合(作为ArrayList)在内部将其支持数组声明为Object[],因此它们可以存储任何内容,甚至是它们的泛型定义不允许存储的类型。

    松安民
    2023-03-14

    1.每次将值存储到数组中时,编译器都会插入一个检查。然后在运行时验证值的类型是否等于数组的运行时类型。

    2.引入了泛型。泛型是不变的,可以在编译时验证。(在运行时,泛型类型被擦除)。

    3、以下是一个失败案例的例子(来自维基百科):

    // a is a single-element array of String
    String[] a = new String[1];
    
    // b is an array of Object
    Object[] b = a;
    
    // Assign an Integer to b. This would be possible if b really were
    // an array of Object, but since it really is an array of String,
    // we will get a java.lang.ArrayStoreException.
    b[0] = 1;
    

    编译器无法检测到第三条语句是否会导致ArrayStoreException。关于第三条语句,编译器看到我们正在向对象[]数组添加一个整数。这是完全合法的。

    背景/推理(来自维基百科)

    Java和C的早期版本不包括泛型(也称为参数多态性)。在这样的设置中,使数组不变就排除了有用的多态程序。例如,考虑编写一个函数来洗牌一个数组,或者编写一个函数来使用对象测试两个数组是否相等。元素上的等于方法。实现不依赖于存储在数组中的元素的确切类型,因此应该可以编写一个在所有类型的数组上工作的函数。类型的函数很容易实现

    boolean equalArrays (Object[] a1, Object[] a2);
    void shuffleArray(Object[] a);
    

    但是,如果数组类型被视为不变量,则只能对类型为Object[]的数组调用这些函数。例如,不能洗牌字符串数组。

    因此,Java和C#都协变地处理数组类型。例如,在C#中,字符串[]是对象[]的子类型,在Java字符串[]是对象[]的子类型

    陆展
    2023-03-14

    >

  • 与泛型不同,数组的类型信息存储在运行时。从一开始,这就是Java的一部分。在运行时,AA[]可以与BB[]区分开来,因为JVM知道它们的类型。

    一个ArrayList(和集合框架的其余部分)使用泛型,这会受到类型擦除的影响。在运行时,泛型类型参数不可用,因此ArrayList

    编译器只知道arr2是一个AA[]。如果您有一个AA[],编译器只能假设它可以存储一个AA。编译器不会检测到类型安全问题,因为您将AA放置在真正的BB[]中,因为它只看到AA[]引用。与泛型不同,Java数组是协变的,因为BB[]AA[],因为BBAA。但这引入了您刚刚演示的可能性-ArrayStoreException,因为arr2引用的对象实际上是BB[],它不会处理AA作为元素。

  •  类似资料:
    • 问题内容: 好的,这是真实的情况:我正在编写一个应用程序,并且有一个表示某种类型文件的类(在我的情况下,这是照片,但是细节与问题无关)。Photograph类的每个实例对于照片的文件名应该是唯一的。 问题是,当用户告诉我的应用程序加载文件时,我需要能够确定文件何时已加载,并使用该文件名的现有实例,而不是在同一文件名上创建重复的实例。 对我来说,使用记忆化似乎是一种好情况,并且有很多这样的例子,但是

    • 数组中的所有字符串都由单独的标记定义,如下所示: 我声明了以下方法: 在我的地图上,我创建了一个搜索字段,我只想在数组的字符串中执行搜索。因此,当我按search按钮定位用户引入的标记名称时,将调用locateFromString方法。在我看来,这个方法应该查看用户引入的标记是否存在于数组中。如果它存在,则应该使用gotoLocation方法定位它,如果不存在,则应该显示Toast表示搜索的标记不

    • 问题内容: 因此,我已经构建了该程序来构建不同的楼梯案例。本质上,问题是:给定整数N,您可以建立楼梯的几种不同方式。确保N大于3且小于200。任何先前的步骤都不能大于其后续步骤,否则会破坏楼梯的目的。 所以给定N = 3,您可以建立一个楼梯:2步,然后再步1步 给定N = 4,您可以建立一个楼梯:3步,然后再步1步 给定N = 5,您可以构建两个楼梯:3步,然后2步,或4步,然后1步。 我的方法在

    • 问题内容: 我有一个保存某种类型的变量,我需要获取一个保存相应数组类的变量。我能想到的最好的办法是: 有没有一种方法可以在不创建新实例的情况下进行操作? 问题答案: 从Java 12开始,在java.lang.Class上提供了方法。接着就,随即:

    • 问题内容: 运行前三行会发出; 如何得到 ???要长吗?我可以解析该字符串,然后执行Class.forname(),但这很糟糕。简单的方法是什么? 问题答案: 写就好了 从JavaDoc: 返回表示数组的组件类型的信息。如果此类不表示数组类,则此方法返回。

    • 我想记住我的网站(SP)的第一次请求的url请求参数,并在IdP响应后使用它们。 我正在使用spring-saml扩展并考虑relayState属性,但找不到如何使用请求中的参数构建它的示例。 我需要在sso认证过程后重定向用户到目标页面(应用程序模块),这取决于第一个请求中的内容。