在回答有关此问题的问题时:https://stackoverflow.com/a/9872630/82609
我尝试执行以下操作:
Comparator<String>[] comparators = new Comparator[] {...};
有效!但以下情况并非如此:
Comparator<String>[] comparators = new Comparator<String>[] {...};
在相关问题上,我做出了假设:
我想这是因为最初阵列合同可能是某种东西 像这样:
如果您创建一个X类型的数组,您将永远无法放置 其中的任何东西都是非 - 非X.如果你尝试,你将得到一个ArrayStoreException
因此,允许使用泛型创建的数组会导致不同 规则如:
如果您创建一个类型为
X<Y>
的数组,您将永远无法做到 放任何不是X的东西。如果你尝试,你会得到一个 ArrayStoreException信息。但是由于类型擦除,您可以添加X<Y>
和X<Z>
个对象!
但是考虑一下,这真的是一个问题:
Comparator<String>[] comparators = new Comparator<String>[] {...};
我真的不明白为什么不可能,因为使用这样的东西会:
最后我们可以使用具有泛型类型引用的数组,并且由于不可能创建具有泛型类型的数组,我想很多人甚至不知道它是可能的。
我只是想知道是否有人知道这个选择背后的原因?
这有点像强迫人们使用List<String> = new ArrayList();
而非使用List<String> = new ArrayList<String>();
但请注意以下内容仍然合法且导致相同:
List<String>[] stringLists = new List[1];
List<Integer> intList = Arrays.asList(42);
Object[] objects = stringLists;
objects[0] = intList;
String s = stringLists[0].get(0);
但是,它会在编译时生成未经检查的强制转换警告,正如您所提到的,在运行时会产生ClassCastException。
答案 0 :(得分:4)
我知道你来自哪里(从实际意义上说我基本同意),但我认为存在差异会激发当前形势。
正如您所提到的,擦除意味着通用参数在运行时不可用,因此在编译时检查类型(对于List<String>
或您的Comparator<String>[]
)。重要的是,这是基于变量的通用参数。
另一方面,数组在运行时检查它们的参数类型,当它们被插入时,如果它们被误用,它们可以抛出ArrayStoreException
(通常是由于滥用它们的协方差)。因此,数组需要能够在内部执行两个项目符号点检查,当然它们无法在运行时检查泛型参数。因此,实例化通用数组是没有意义的,因为数组必须完全忽略泛型参数,这最多会产生误导。
也就是说,将这样的数组分配给参数化引用是有意义的,因为编译器可以执行泛型检查。而且您认为这涵盖了所有基础,并确保检查泛型类型(只要变量参数化正确),您就是正确的。
这个选择背后的底线原因,以及为什么数组在这方面与集合不同,是数组需要在插入时实际检查它们的参数类型,而集合只需要你的意思并允许类型错误稍后传入ClassCastException
。
答案 1 :(得分:1)
引用伟大的Effective Java Second Edition页面120:
为什么通用数组创建是非法的 - 不会编译!
List<String>[] stringLists = new List<String>[1]; // (1)
List<Integer> intList = Arrays.asList(42); // (2)
Object[] objects = stringLists; // (3)
objects[0] = intList; // (4)
String s = stringLists[0].get(0); // (5)
让我们假装创建通用数组的第1行是合法的。第2行创建和
初始化包含单个元素的List<Integer>
。 3号线存储了
List<String>
数组成一个Object数组变量,这是合法的,因为数组
是协变的。第4行将List<Integer>
存储到the的唯一元素中
对象数组成功,因为泛型是通过擦除实现的:
List<Integer>
实例的运行时类型只是List,而运行时类型是
List<String>[]
实例为List[]
,因此此分配不会生成
ArrayStoreException
。现在我们遇到了麻烦。我们存储了List<Integer>
将instance转换为声明仅包含List<String>
个实例的数组。在
第5行,我们从这个数组中的唯一列表中检索唯一元素。编译器
自动将检索到的元素强制转换为String,但它是一个Integer,所以我们得到了
运行时ClassCastException
。为了防止这种情况发生,第1行
(创建通用数组)会生成编译时错误。