据我所知,new ArrayList<String>()
被转换为原始类型,并且使用了大量的语法糖来假装Object的这个ArrayList就像String的ArrayList。 Java将此称为类型擦除。
这是我用Java写的:
public static void main(String[] args) {
ArrayList<String> stringList = new ArrayList<>();
stringList.add("foo");
String s = stringList.get(0);
}
当我反编译字节码时,我得到了这个:
public static void main(String[] args) {
ArrayList<String> stringList = new ArrayList();
stringList.add("foo");
String s = (String)stringList.get(0);
}
为什么不能new T[]
自动转换为(T[]) new Object[]
,使用相同的&#34; shtick&#34; 编译器拉取类型擦除?
请不要向我推荐这个问题:What's the reason I can't create generic array types in Java?特别是这条评论:
这个问题比答案更深刻,更进一步 需要调查。正如你所说,类型信息被删除了 编译代码我们在两个泛型类型之间没有区别 - 我们都是 有基类型 - 为什么
T[]
- 编译为Object[]
。在这种情况下 一切都会很好 - 数组会记住它是用它创建的 对象类型并将保存所有类型。但对我来说真实 问题是数组是协变的意味着Animal[]
可以 分配给Object[]
。另一方面,泛型不是ArrayList<Animal>
无法分配到ArrayList<Object>
因为这个逻辑存在缺陷!
这里有两个进程。
编译器对ArrayList<String>
强制实施&#34;仿真&#34; 不变性。
编译器将Object
转换为T
。
同样,为什么不能使用形成所有泛型的简单语法糖在Java中实现泛型数组,同时保持数组的正常协方差?
答案 0 :(得分:8)
可以通过两种方式强制执行类型安全:在运行时或编译时。
Arrays在运行时强制执行它:
Object[] array = new Integer[1];
array[0] = "";
// ArrayStoreException!
泛型在编译时强制执行:
List<Object> list1 = new ArrayList<Integer>();
// does not compile!
List<? extends Object> list2 = new ArrayList<Integer>();
list2.add(1);
// does not compile!
因为T[]
是一个数组,所以它必须是协变的,但是由于类型擦除,在运行时没有办法检查类型,如下例所示:
Object[] array = (T[])new Object[1];
array[0] = 1;
由于在运行时array
实际 类型为Object[]
,因此无论T
恰好是什么,此代码都将无错误地编译和运行。这导致heap pollution,这通常会导致您的代码在某个不可预测的地方或时间失败,从而难以诊断和修复。因此警告。
答案 1 :(得分:4)
此代码抛出异常:
static <T> T[] newArray() {
return (T[]) new Object[0];
}
static {
// throws ClassCastException,
// because Object[] is not a String[].
String[] a = newArray();
}
这是因为String[]
和Object[]
在运行时实际上有不同的类型。 (这与泛型不同:例如,List<String>
和List<Object>
具有相同的类。)
根据您的建议,方法newArray
将如下所示:
static <T> T[] newArray() {
return new T[0];
}
这显然不是错的,但确实如此。我认为这种变化只会导致更多的实际收益混乱。
可以想象,他们可以完全改变数组的工作方式,因此他们只有编译时子类型,并且在运行时只是Object[]
。不过,这本来是一个非常重大的变化,并且会打破旧代码。
就个人而言,我认为他们应该只添加一个通用class Array<E>
,它与通用数组的用途相同。实际上首先需要一个是非常罕见的。