我正在使用Eclipse,当我导航List.class的反汇编时,我看到如下:
<T> T[] toArray(T[] a);
前面的
<T>
在这里实际上是什么意思?它是返回类型的一部分吗?我猜返回值是T[]
。在什么情况下,一个函数需要声明像
<T> T[]
作为函数签名?
这定义了一个方法,其中一些类型不是固定的,而是泛型的。
该方法的非泛型版本为
String[] toArray(String[] a);
意味着您提供一个String[]
数组并获得一个String[]
数组。
给定的方法将String
替换为泛型T
,这意味着结果类型现在取决于所提供的类型(*),结果数组类型与作为数组参数提供的类型相同。
此处相关的数组类型不必(完全)对应于整个List的泛型类型(List<E>
中的E
),而是特定于此方法,因此必须使用此方法声明,即<T> T[] toArray(T[] a);
中的<T>
那么,把<T> T[] toArray(T[] a);
分解成它的各个部分:
<T>
声明以下方法具有特定于方法的泛型T
。T[]
表示返回值是该泛型类型的数组。toArray(...)
命名方法。T[] a
声明方法需要一个数组类型的参数。
对于Java泛型,编译器需要在任何给定的方法调用中找到替换T
的特定类型,例如
??? myArray = myList.toArray(new String[17]);
在这里,参数给出了信息:提供了String[]
数组,期望T[]
数组,因此T
必须是String
,现在编译器知道在结果类型T[]
中T
也必须用String
替换,给出String[]
结果类型,这意味着???
必须是String[]
或兼容的,否则你会得到编译器错误。
除了这种基于演绎的类型匹配之外,还有一种(很少使用的)语法用于显式指定泛型类型。那就是
String[] myArray = myList.<String>toArray(new String[17]);
<String>
中缀显式地告诉编译器T
的替换。在大多数情况下,这种显式规范是多余的,因此您几乎不会在实际代码中看到这种语法。
(*) Java泛型在编译时完全求值,因此声明的泛型效果仅基于编译器看到的类型。例如,在
中Object[] myObjectArray = new String[0];
??? myResultArray = myList.toArray(myObjectArray);
???
必须是Object[]
,尽管在运行时,String[]
数组将生成。
请记住,泛型背后的思想是在类型上参数化类,并且在实例化类时指定该类型。<T>
是类型参数——也就是说,你这样写是为了说明这个类/方法是在运行时指定的某种类型T
上参数化的。