考虑以下代码:
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时,它可能会导致编译器错误,而不是在运行时检测到它。
谢谢。
数组确实显式地记住它们的类型,您甚至可以在运行时获得它(array.getClass(). getComponentType())。
在存储到阵列时,VM将检查要存储的元素是否与阵列的组件类型兼容。如果不是,则会得到ArrayStoreException。
集合(作为ArrayList)在内部将其支持数组声明为Object[],因此它们可以存储任何内容,甚至是它们的泛型定义不允许存储的类型。
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字符串[]是对象[]的子类型
>
与泛型不同,数组的类型信息存储在运行时。从一开始,这就是Java的一部分。在运行时,AA[]
可以与BB[]
区分开来,因为JVM知道它们的类型。
一个ArrayList
(和集合框架的其余部分)使用泛型,这会受到类型擦除的影响。在运行时,泛型类型参数不可用,因此ArrayList
编译器只知道
arr2
是一个AA[]
。如果您有一个AA[]
,编译器只能假设它可以存储一个AA
。编译器不会检测到类型安全问题,因为您将AA
放置在真正的BB[]
中,因为它只看到AA[]
引用。与泛型不同,Java数组是协变的,因为BB[]
是AA[]
,因为BB
是AA
。但这引入了您刚刚演示的可能性-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认证过程后重定向用户到目标页面(应用程序模块),这取决于第一个请求中的内容。