你的问题是有道理的。总之,方法参考确实使用原始类型(或应该使用原始类型),究其原因,为什么通用阵列的创建是被禁止的,当使用方法的引用,因此,能够静静地创建一个函数创建仍适用通用数组显然违反了语言设计的意图。
为什么禁止创建泛型数组的原因是数组类型继承,源于泛型前一代,与泛型类型系统不兼容。即你可以这样写:
IntFunction<List<String>[]> af = List[]::new; // should generate warning
List<String>[] array = af.apply(10);
Object[] objArray = array;
objArray[0] = Arrays.asList(42);
List<String> list = array[0]; // heap pollution
在这个地方,但必须强调的是,相反这里的一些答案,编译器不上表达List[]::new
执行类型推断推断出通用元素类型List<String>
。这很容易证明,通用阵列的创建仍然是被禁止的:
IntFunction<List<String>[]> af = List<String>[]::new; // does not compile
由于List<String>[]::new
是非法的,如果List[]::new
被接受而不发出警告,通过推断它是有效的非法List<String>[]::new
才是怪事。
JLS §15.13明确规定:
如果一个方法参考表达式具有形式数组类型::
new
,然后数组类型必须表示一个类型是reifiable(§4.7)或编译时错误发生。
这已经意味着List<String>[]::new
是非法的,因为List<String>
不reifiable,而List<?>[]::new
是合法的,因为List<?>
是reifiable和List[]::new
是合法的,如果我们考虑List
是一个原始类型,为原料类型List
是可验证的。
然后§15.13.1规定:
如果该方法引用表达式具有形式数组类型::
new
,单个名义方法被认为。该方法具有int
类型的单个参数,返回ArrayType,并且没有throws
子句。如果n = 1,这是唯一可能适用的方法;否则,没有可能适用的方法。
换句话说,所述List[]::new
表达的行为以上是相同的,如果你写:
IntFunction<List<String>[]> af = MyClass::create;
…
private static List[] create(int i) {
return new List[i];
}
不同之处在于该方法create
只是概念上的。事实上,通过这个明确的方法声明,在create
方法中只有原始类型警告,但没有未检查有关在方法参考中将List[]
转换为List<String>[]
的警告。所以这是可以理解的,编译器在List[]::new
的情况下会发生什么情况,其中使用原始类型的方法只是名义上的,即在源代码中不存在。
但由于没有选中警告是JLS §5.1.9, Unchecked Conversion明显违反:
让G
名称与ň类型参数的泛型类型声明。
有从原始类或接口类型(§4.8)G
任何参数化的类型的形式G<T₁,...,Tₙ>
的未经检查的转换。
存在从原始数组类型G[]ᵏ
到格式为G<T₁,...,Tₙ>[]ᵏ
的任何数组类型的未经检查的转换。 (记号[]ᵏ
指示ķ尺寸数组类型。)
使用未经检查的转换会导致编译时未检查警告除非所有类型的参数T
ᵢ(1≤我≤Ñ)是无限通配符(§4.5.1),或未经检查的警告被SuppressWarnings
注释(§9.6.4.5)所抑制。
所以,List[]
到List<?>[]
转换是合法的,因为List
是参数采用无界通配符,但是从List[]
到List<String>[]
转换,必须出示选中警告,这是至关重要的位置,因为使用List[]::new
不会生成原始类型以显式创建方法出现的警告。没有原始类型警告似乎不是一个违反(据我了解§4.8),这不会是一个问题,如果javac
创建所需的未经检查警告。
有趣...编写'n - > new Test [n];'而不是'Test [] :: new'(应该基本相同)会再次给你一个未经检查的编译警告 – Roland
@Roland它们都是也被排除在相同的字节码上。确实有趣 – Eugene
@Eugene:好吧,对于正式的规则,通常无关于语言结构的解构。但是编译器显然应该在这里发出* unchecked *警告。 – Holger