map*_*ple 12 java arrays generics syntax
第一个代码:
List<Integer>[] array = (List<Integer>[]) new Object[size];
Run Code Online (Sandbox Code Playgroud)
它将给出以下异常:
java.lang.ClassCastException: class [Ljava.lang.Object; cannot be cast to class [Ljava.util.List; ([Ljava.lang.Object; and [Ljava.util.List; are in module java.base of loader 'bootstrap')
为什么会这样呢?我只是按照有效Java第三版 132页的方法进行操作:
第二码:
E[] array = (E[]) new Object[size];
Run Code Online (Sandbox Code Playgroud)
但是我发现以下代码有效
第三码:
List<Integer>[] array = (List<Integer>[]) new List[size];
Run Code Online (Sandbox Code Playgroud)
我的问题:
例如:为什么以下代码运行良好,但是第一个代码错误?
public class Test<E>{
E[] array;
public Test(){
array = (E[]) new Object[10];
}
public E set(E x){
array[0] = x;
System.out.println(array[0]);
return array[0];
}
public static void main(String[] args){
Test<List<Integer>> test = new Test<>();
List<Integer> list = new ArrayList<>();
list.add(1);
test.set(list);
}
}
Run Code Online (Sandbox Code Playgroud)
第四码:
List<Integer>[] array = new List<Integer>[size];
第一码
Run Code Online (Sandbox Code Playgroud)List<Integer>[] array = (List<Integer>[]) new Object[size];
第一个代码失败的原因是因为强制转换不会更改数组的实际类型,它只是使编译器接受该代码为有效代码。想象一下,如果您还有对底层对象数组的引用:
final int size = 2;
Object[] objectArr = new Object[size];
List<Integer>[] integerArr = (List<Integer>[]) objectArr; // Does not work
objectArr[0] = "foobar";
List<Integer> i = integerArr[0]; // What would happen ??
Run Code Online (Sandbox Code Playgroud)
上面的代码可以很好地编译,因为您正在强制编译器通过强制转换接受它。但是您已经知道为什么强制转换在运行时工作会成为问题:您最终会得到一个List<Integer>[]现在包含的String,这没有任何意义。因此该语言不允许这样做。
第二码
Run Code Online (Sandbox Code Playgroud)E[] array = (E[]) new Object[size];
Java中的泛型有点奇怪。由于各种原因(例如向后兼容),泛型基本上会被编译器擦除,并且(通常)不会出现在已编译的代码中(Type Erasure)。相反,它将使用一系列规则(JLS spec)来确定应在代码中使用哪种类型。对于基本的无绑定通用名;这种类型将是Object。因此,假设没有限制E,编译器将第二个代码更改为:
Object[] array = (Object[]) new Object[size];
Run Code Online (Sandbox Code Playgroud)
因此,由于擦除后两个数组的类型完全相同,因此在运行时没有问题,并且强制转换基本上是多余的。
值得注意的是,这只有E在不受限制的情况下才有效。例如,这将在运行时失败,并带有ClassCastException:
public static <E extends Number> void genericMethod() {
final int size = 5;
E[] e = (E[]) new Object[size];
}
Run Code Online (Sandbox Code Playgroud)
那是因为E将被擦除为Number,您将得到与第一个代码相同的问题:
Number[] e = (Number[]) new Object[size];
Run Code Online (Sandbox Code Playgroud)
在使用代码时,请务必谨记擦除。否则,您可能会遇到代码行为与预期不同的情况。例如,以下代码无例外地编译和运行:
public static <E> void genericMethod(E e) {
final int size = 2;
Object[] objectArr = new Object[size];
objectArr[0] = "foobar";
@SuppressWarnings("unchecked")
E[] integerArr = (E[]) objectArr;
integerArr[1] = e;
System.out.println(Arrays.toString(integerArr));
System.out.println(e.getClass().getName());
System.out.println(integerArr.getClass().getName());
}
public static void main(String[] args) {
genericMethod(new Integer(5)); // E is Integer in this case
}
Run Code Online (Sandbox Code Playgroud)
第三码
Run Code Online (Sandbox Code Playgroud)List<Integer>[] array = (List<Integer>[]) new ArrayList[size];
与上述情况类似,第三个代码将被擦除为以下内容:
List[] array = (List[]) new ArrayList[size];
Run Code Online (Sandbox Code Playgroud)
没问题,因为它ArrayList是的子类型List。
第四码
Run Code Online (Sandbox Code Playgroud)List<Integer>[] array = new ArrayList<Integer>[size];
以上内容将无法编译。spec明确禁止使用类型具有泛型类型参数的数组进行创建:
如果正在初始化的数组的组件类型不可修改(第4.7节),则是编译时错误。
具有非无限通配符(?)的通用参数的类型不满足任何可验证性条件:
仅当以下条件之一成立时,类型才是可更改的:
- 它指的是非泛型类或接口类型声明。
- 它是一个参数化类型,其中所有类型参数都是无界通配符(第4.5.1节)。
- 这是原始类型(第4.8节)。
- 它是原始类型(第4.2节)。
- 它是一种数组类型(第10.1节),其元素类型是可修改的。
- 它是一个嵌套类型,其中对于每个用“。”分隔的类型T,T本身都是可确定的。