【发布时间】:2012-07-23 21:09:36
【问题描述】:
简而言之,这不会编译:
public <A> void test() {
A[] temp = new A[]{};
}
是因为向后兼容性的问题,还是语言设计中的一些基本因素阻止了它?
【问题讨论】:
-
“是不是语言设计中的一些基本因素阻止了它?” - 显然,是的。
标签: java language-design language-details
简而言之,这不会编译:
public <A> void test() {
A[] temp = new A[]{};
}
是因为向后兼容性的问题,还是语言设计中的一些基本因素阻止了它?
【问题讨论】:
标签: java language-design language-details
底线是表示数组的类必须知道组件类型。因此 Class 对象上的方法:
public Class<?> getComponentType()
Returns the Class representing the component type of an array. If this class does not represent an array class this method returns null.
所以你什么时候会尝试:
A[] a = new A[0];
在编译时,很明显我们不知道类型,因为它是一个泛型参数。在运行时,由于类型擦除,我们不知道类型。所以实例化数组是不可能的。
认为上面的语句等价于:
A[] a = (A[])Array.newInstance(???, 0);
并且由于类型擦除,我们无法在运行时获取 A 的类。
有人问为什么不让编译器简化为 Object[] 或 Number[] 或类似的东西?
这是因为会根据组件类型返回不同的 Class。所以:
new Object[0].getClass()
new Integer[0].getClass()
不是同一个类。特别是类上的“getComponentType()”方法会返回不同的值。
因此,如果您将其简化为 Object[] 而不是 A[],您实际上并没有返回 A[] 类型的东西,而是返回了 Object[]。 Object[] 不能大小写为 Integer[] 并且会产生 ClassCastException。
【讨论】:
<A>和Object[] a在字节码中的A[] a有区别还是都编译成相同的字节码?
在 Java 中,数组和泛型的类型系统是不兼容的。 存在两个主要差异领域:动态与静态类型检查和协方差。
泛型是静态检查的:也就是说,编译器确保类型定义是一致的。 “类型擦除”是一种妥协,以确保 JVM 中的向后兼容性。编译后,泛型类型定义不再可用。例如。 List<T> 变为 List。
相比之下,数组是动态类型检查的。考虑以下示例:
String strings[] = {"a","b","c"};
Object simple[] = strings;
simple[0] = new Object(); // Runtime error -> java.lang.ArrayStoreException
协变是容器基于内容的继承关系。 给定类型 A、B 和容器 C,如果 B isAssignableFrom(A) => C isAssignable C。
Java 中的数组是协变的,在前面的示例中,鉴于 Class<Object>.isAssignableFrom(Class<String>) => Object[] is assignable from String[]
相比之下,泛型类型在 Java 中不是协变的。使用相同的示例:
List<String> stringList = new ArrayList<String>();
List<Object> objectList = stringList; // compiler error - this is not allowed.
鉴于泛型实现的类型擦除,类型信息会在转换中丢失,因此如果您可以创建泛型类型的数组,动态类型检查将受到影响。
进一步了解这些问题的复杂性和影响:Arrays in Java Generics
【讨论】:
这与new A() 无法工作的原因相同(或几乎相同):您期望编译器知道A 的运行时类型,而它显然无法知道。如果 Java 泛型类似于 C++ 模板,这会工作,其中为涉及A 的模板的每个实例化生成新代码。
【讨论】:
new A()有什么相似之处?你能详细说明一下吗?
A[] 是一个独特的类型,就像 A 本身一样。它的一个实例知道它的类型。这与List<A> 非常不同,后者在运行时只是一个List。
new A[] {} 不涉及创建任何对象。我不清楚与new A() 的联系。
new 不 涉及创建对象。 new A[0] 创建一个 A[] 类型的新对象。
new A() 需要默认构造函数,而new A[]{} 不需要。所以我觉得不一样。
是的,有一个根本原因,归结为type erasure。
考虑以下 sn-p:
A[] temp = new A[5]; // Assume this would compile.
Object[] objects = temp; // This is allowed, since A extends Object.
objects[0] = new String(); // This does *not* throw an ArrayStoreException
// due to type erasure since the original type of A
// is now Object.
A t = temp[0]; // Now this seemingly innocent line would *fail*.
相关问题:
【讨论】:
List<Foo> fooList; 会遇到同样的问题,然后将 fooList.toArray(new Foo[fooList.size()]) 传递给 public <A> void test(A[] as);Generics 和数组不适合,因为数组不是类型安全的。