“如何使用数组”;记住;它们在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
时,它可能会导致编译器错误,而不是在运行时检测到它AA[]
与BB[]
区分开来,因为JVM知道它们的类型ArrayList
(以及集合框架的其余部分)使用泛型,这会受到类型擦除的影响。在运行时,泛型类型参数不可用,因此无法区分ArrayList
和ArrayList
;它们都只是JVM的ArrayList
sarr2
是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;