今天看java核心卷一,看到一个这样的局限性:“不能构建参数化类型的数组”。也就是不能构建泛型类型的数组,比如书上new Pair<String>[10]。
原因就是数组存储时会考虑这个数组创建时的元素类型,如果不对会报错。不过这个过程是运行时才会出现的,所以如果只是编译那么是不会有问题的,所以书上说“能够通过数组存储的检查”
可以看看字节码来验证这一点
public class Hello
{
public static void main(String[] args) {
var arr = new String[10];
Object[] arr1 = arr;
arr1[0] = new Object();
}
}
这段对应的字节码如下
明显没有检查类型,没有checkcast这一步骤。因为编译器看起来,arr1就是Object[]类型的,和String没关系。
不过,如果真的运行起来会抛出一个ArrayStoreException,原因当然就是数组本身记住了自己是String[]类型的,但是却收到了一个Object类型。
但是泛型就不一样了。因为JVM不知道泛型这个东西,他只知道原始类型,所以即使你传入的元素参数类型(例如Pair<String>和Pair<Integer>在JVM看起来是一样的)不一样它也检查不出来。看这个例子
public class Hello
{
public static void main(String[] args) {
var arr = (Pair<String>[]) new Pair<?>[10];
Object[] arr1 = arr;
arr1[0] = new Pair<Integer>(10, 10);
}
}
也是个书上的例子,这里arr就是个Pair<String>[]类型的数组,强转Object[]类型,当然没问题,然后给arr1[0]赋值Pair<Integer>类型实例,显然JVM看不出问题,因为对JVM来说,这就是个Pair[]类型的数组,然后你给第0个元素赋了一个Pair类型的实例。当然没问题。
所以说这是不安全的,因为JVM不知道什么参数类型,所以Pair<String>和Pair<Integer>就是一个东西,传给数组也不会报错。
但是,这和不是数组的情况是有区别的,例如这个
public class Hello
{
public static void main(String[] args) {
var p1 = new Pair<String>("123", "123");
Pair<Integer> p2 = p1;
}
}
就会报错,编译都通不过。这个原因最开始就说了,是判断类型的过程不同,这里这个是编译器处理的,编译器是知道参数类型这个概念的,所以Pair<String>和Pair<Integer>是两个东西,编译觉得不行,你这不能转,所以就报错;但是数组的存储类型是在运行时由JVM检查的,对上面那个(第二个,arr和arr1那个)例子,编译器觉得arr1是个Object类型的数组,所以你给他传Pair<Integer>没问题,然后JVM觉得你这数组是个Pair类型的数组,所以传个Pair类型的元素,也没问题,所以参数类型这个东西编译器和JVM都没注意到,所以说不安全。
不过,如果你在命令行编译,还是会有一个小注解的,这样子