public class Plate<T> {
private T item;
public Plate(T t){
item=t;
}
public void set(T t){
item=t;
}
public T get(){
return item;
}
public static void main(String[] args) {
Plate<String> [] arr=new Plate[10];
//Plate<String> [] arr2=new Plate<>[10];//编译错误
Plate<Integer> plate=new Plate<>(1);
Object[] arr1=arr;
arr1[0]=plate;
String a=arr[0].get();
}
}
请问为什么
Plate<String> [] arr=new Plate[10]
不报错,而
Plate<String> [] arr2=new Plate<>[10]
编译错误,
这两语句有什么不同?
-------------------------------后续补充--------------------------------
关于
in generics instantiation of arrays of parametrized types are illegal.
我是这样理解的,以一个例子说明一下:
static void test() {
Pair<Integer,Integer>[] intPairArr = new Pair<Integer,Integer>[10] ; // error
addElements(intPairArr);
Pair<Integer,Integer> pair = intPairArr[1];
Integer i = pair.getFirst();
pair.setSecond(i);
}
static void addElements( Object[] objArr) {
objArr[0] = new Pair<Integer,Integer>(0,0);
objArr[1] = new Pair<String,String>("",""); // should fail with ArrayStoreException
}
倘若支持,则我们调用addElements方法时,将其向上转型为Object[](数组支持协变,故编译通过),则继续添加元素时,则会编译通过,这样在运行时会出现arrayStoreException,Java引入泛型其中很重要的一点就是为了安全,将运行时的异常在编译时就暴漏出来,避免出现由于数组支持协变而导致的一些异常在编译期间无法发现,可以说,不支持泛型对象数组的创建就是因为数组有协变这一特性。
而答案中所说到的关于Java不支持泛型数组很好理解,举例说明:
T [] arr=new T[10];
这里我们假设T为String,由于Java的泛型采用擦除机制,只作用于编译时,编译之后则T都转化为Object,实际上创建的都是Object数组,则通过桥接方法转化为String时,则明显不对,故不支持泛型数组的创建。
我们可以通过迂回的方式来创建泛型数组,举例说明:
public T[] createArray(T[] arr){
T[] array= (T[]) new Object[arr.length];
for (int i = 0; i <arr.length ; i++) {
array[i]=arr[i];
}
return array;
}
实际上将传入的泛型数组copy到对应的Object数组中了。
最后一个问题,什么原因导致了Java支持数组协变这一可能出现问题的特性呢?
支持数组协变是为了方便 方法重载 —— 因为 Java 刚出现的时候,Java 还没有引入泛型,如果不支持数组协变,那么对数组的某个操作,对不同类型便需要新写一个方法,就像这样:
而如果支持协变,那么便可以统一为:
后来 Java5 之后 Java 引入了泛型机制,所以对数组的处理已经可以统一抽象为泛型:
所以现在数组协变的地位就很尴尬了 —— 大家都嫌弃它 —— 也是个历史遗留的包袱吧。