我知道泛型类型信息在编译时在Java中被删除,因此无法创建泛型类型的数组(因为在运行时,不可能有效地强制插入到数组中的类型)。
但为什么不例外呢?为什么不留下数组的泛型类型信息(仅适用于它们)?
这背后的设计决定是什么?在我看来,它会让生活更轻松,有可能做到这一点。
T[] genericArray = new T[10];
答案 0 :(得分:4)
简答:
这是因为泛型是帮助编译器帮助您捕获类型的元数据
错误和所有内容都编译为使用最低公分母
(通常为Object
)并输入类型。自阵列以来,这不是用数组完成的
是他们自己的课程。即ArrayList<String>
和ArrayList<Number>
ArrayList
两者都有类String
,但是String[]
的数组有类Number
和一个数组
Number[]
已上课Object
。
答案很长:
编译时,使用泛型的所有内容都将使用最少
共同点(通常为public class Generics {
public static <T> void print(T what) {
System.out.println(what);
}
public static <T extends Number> void printNumber(T what) {
System.out.println(what);
}
public static void main(String[] args) {
Arrays.stream(Generics.class.getDeclaredMethods())
.filter(m -> m.getName().startsWith("print"))
.forEach(Generics::print);
}
}
)。这表现在
以下代码:
public static void Generics.print(java.lang.Object)
public static void Generics.printNumber(java.lang.Number)
打印:
Object
因此,我们可以看到,当它被编译时,它被编译为分别适用于Number
和ArrayList<String> list = new ArrayList<>();
list.add("foo");
ArrayList<Object> list2 = (ArrayList<Object>)(Object)list;
list2.add(Integer.valueOf(10));
System.out.println(list2.get(0));
System.out.println(list2.get(1));
的方法。
这就是编译和运行的原因:
foo
10
如果你试试,你会看到它打印
ArrayList<String>
因此,通过向下/向上转换,我们将ArrayList<Object>
转换为String[]
- 如果ArrayList实际将其内容存储在数组中,那么这是不可能的Object[]
类型而不是System.out.println(list.get(0));
System.out.println(list.get(1));
。
注意尝试
ClassCastException
将导致public static void doThingsWithList() {
ArrayList<String> list = new ArrayList<>();
list.add("");
String s = list.get(0);
print(s);
}
。这暗示了究竟是什么
编译器。
请查看以下代码:
public static void doThingsWithList();
Code:
0: new #11 // class java/util/ArrayList
3: dup
4: invokespecial #12 // Method java/util/ArrayList."<init>":()V
7: astore_0
8: aload_0
9: ldc #13 // String
11: invokevirtual #14 // Method java/util/ArrayList.add:(Ljava/lang/Object;)Z
14: pop
15: aload_0
16: iconst_0
17: invokevirtual #15 // Method java/util/ArrayList.get:(I)Ljava/lang/Object;
20: checkcast #16 // class java/lang/String
23: astore_1
24: aload_1
25: invokestatic #17 // Method print:(Ljava/lang/Object;)V
28: return
编译后,它变成了这个字节码:
20
正如您在ArrayList.get
行上看到的那样,String
的结果实际上是投放到ClassCastException
。
因此,泛型只是语法糖,变成自动类型转换,还有一个额外的好处,即编译器可以使用这个语法糖来检测在运行时会导致String[]
的代码。
现在,为什么编译器可以对Object[]
和public <T> T[] addToNewArrayAndPrint(T item) {
T[] array = new T[10];
array[0] = item;
System.out.println(array[0]);
return array;
}
执行相同的操作?无法转身
public <T> T[] addToNewArrayAndPrint(T item) {
Object[] array = new Object[1];
array[0] = item;
System.out.println((T) array[0]);
return array;
}
到
Arrays.equals(addToNewArray("foo"), new String[]{ "foo" });
不。因为那意味着
Object[]
将为false,因为第一个数组将具有类String[]
,第二个数组将具有类Object[]
。
当然,可以更改Java,以便所有数组都是ArrayList<String>
类型,并且所有访问都将使用强制转换,就像使用泛型时一样。但这会破坏向后兼容性,而使用泛型并不是因为ArrayList
与$headers = array('Content-type: multipart/form-data','Authorization: '.$api_key,);
$curl_post_data = array('employee_id' => $employee_id,'files' => $_FILES);
$curl = curl_init('http://[...mydomain...]/v1/uploadEquipmentDocument');
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, $curl_post_data);
curl_setopt($curl, CURLOPT_USERPWD, $api_key);
$curl_response = curl_exec($curl);
具有相同的类。
答案 1 :(得分:1)
像new T[10];
这样的构造的问题是T
可能是任何东西,包括Void
(实际上是一个不可实例化的类,会产生编译错误)。
如果你考虑一下,类型是一种语言结构,因此它应该在编译时而不是在运行时发挥作用。在某些情况下,运行时类型信息和某些语言实际实现它是有意义的,但是否需要它并且是“Good Thing™”是值得商榷的。
有关类型擦除的一些有用信息:https://stackoverflow.com/a/21843984/1417546