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

“如何使用数组”;记住;它们在Java中的类型?,java,arrays,generics,arraylist,Java,Arrays,Generics,Arraylist,考虑以下代码: 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; // Arr

考虑以下代码:

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");

    }
}
AA类{}
类BB扩展了AA{}
公共类测试{
公共静态void main(字符串[]args){
BB[]arr=新BB[10];
AA[]arr2=arr;
BB b=新的BB();
AA a=新的AA();
arr2[0]=a;//运行时ArrayStoreException
arr2[1]=b;
List listBB=新的ArrayList();
List listAA=listBB;
add(“helloworld.txt”);
}
}
在上面的示例中,当我尝试
arr2[0]=a
时,我得到了
ArrayStoreException
。这意味着数组记住它必须接受的类型。但是
列表
不记得它们。它只是简单地编译并运行良好。当我检索对象
BB
时,将抛出
ClassCastException

因此,问题是:

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

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

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

  • 谢谢

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

  • ArrayList
    (以及集合框架的其余部分)使用泛型,这会受到类型擦除的影响。在运行时,泛型类型参数不可用,因此无法区分
    ArrayList
    ArrayList
    ;它们都只是JVM的
    ArrayList
    s

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


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

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

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

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

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

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

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

    背景/推理(来源)

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

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

    因此,Java和C#都以协变的方式处理数组类型。例如,在C#中,string[]是object[]的子类型,而在Java中,string[]是object[]的子类型

    3) 是否执行
    AA a=new AA()
    AA a=新BB(),编译器不记得您稍后分配给
    a
    的内容,只记得它声明的类型是
    AA
    。然而,在后一种情况下,实际上可以将
    a
    的值分配给
    BB[]
    的元素,因此
    arr2[0]=a不应为您提供运行时异常。因此,编译器无法提前判断。(此外,您可以尝试在运行时在相应行之间更改
    a
    的值…)

    2) 您是否使用了
    List listAA=listBB,则会出现编译错误。因此,您期望从数组示例中得到的结果——编译时检测出现的不可能赋值——实际上适用于列表!但是,如果省略泛型类型参数,您将得到一个原始类型列表,您可以在不进行合理类型检查的情况下为其分配其他列表。这被认为是早期Java的遗留问题,应该避免。如果您在问题代码下方添加了以下行:

    BB item = listBB.get(0);
    
    你认为它应该/会被编译吗?应该/将
    boolean equalArrays (Object[] a1, Object[] a2);
    void shuffleArray(Object[] a);
    
    BB item = listBB.get(0);
    
    AA[] arr2=arr;
    
    Bike b=new Bike()
    Car c=b;