在Java中,不可能直接创建泛型类型数组:
<tr v-for="icon in icons">
<td><i :class="'fa fa-' + icon.css"></i></td>
<td>{{icon.name}}</td>
</tr>
但是,我们可以使用原始类型执行此操作:
Test<String>[] t1 = new Test<String>[10]; // Compile-time error
在Java 8中,也可以使用构造函数引用:
Test<String>[] t2 = new Test[10]; // Compile warning "unchecked"
为什么编译器在最后一种情况下不显示警告?它仍然使用原始类型来创建数组,对吗?
答案 0 :(得分:28)
你的问题是合理的。简而言之,方法引用确实使用原始类型(或应该使用原始类型)以及为什么禁止创建泛型数组的原因,仍然适用于使用方法引用时,因此,能够以静默方式创建函数创建通用数组明显违反了语言设计的意图。
禁止创建通用数组的原因是源于前Generics时代的数组类型继承与泛型类型系统不兼容。即你可以写:
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
执行类型推断以推导出通用元素类型{{1} }。很容易证明仍然禁止通用数组创建:
List<String>
由于IntFunction<List<String>[]> af = List<String>[]::new; // does not compile
是非法的,如果在没有警告的情况下接受List<String>[]::new
,通过推断它实际上是非法的List[]::new
会很奇怪。
JLS §15.13明确指出:
如果方法引用表达式的格式为 ArrayType
List<String>[]::new
::
,则 ArrayType 必须表示可重新生成的类型(第4.7节) ,或发生编译时错误。
这已经暗示new
是非法的,因为List<String>[]::new
无法恢复,而List<String>
是合法的,因为List<?>[]::new
是可以恢复的,List<?>
是我们认为List[]::new
是原始类型是合法的,因为原始类型 List
是可以恢复的。
然后§15.13.1声明:
如果方法引用表达式的格式为 ArrayType
List
::
,则会考虑使用单一的名义方法。该方法具有类型new
的单个参数,返回 ArrayType ,并且没有int
子句。如果 n = 1 ,这是唯一可能适用的方法;否则,没有可能适用的方法。
换句话说,上面throws
表达式的行为与您编写的行为相同:
List[]::new
除了方法 IntFunction<List<String>[]> af = MyClass::create;
…
private static List[] create(int i) {
return new List[i];
}
只是名义上的。实际上,使用此显式方法声明,create
方法只有原始类型警告,但没有关于{{1}转换的未经检查警告}到方法参考的create
。所以这是可以理解的,在List[]
情况下编译器会发生什么,其中使用原始类型的方法只是名义上的,即在源代码中不存在。
但是,未经检查警告的缺失明显违反了JLS §5.1.9, Unchecked Conversion :
让
List<String>[]
使用 n 类型参数命名泛型类型声明。从原始类或接口类型(§4.8)
List[]::new
中有未经检查的转换到G
形式的任何参数化类型。从原始数组类型
G
到G<T₁,...,Tₙ>
形式的任何数组类型都有未经检查的转换。 (符号G[]ᵏ
表示 k 维度的数组类型。)除非所有类型参数
G<T₁,...,Tₙ>[]ᵏ
ᵢ(1≤ i ≤ n )是无界通配符(§4.5.1),或者[]ᵏ
注释(第9.6.4.5节)禁止未经检查的警告。
因此,将T
转换为SuppressWarnings
是合法的,因为List[]
使用无界通配符参数化,但从List<?>[]
转换为List
必须生成未经检查的警告,这在此至关重要,因为使用List[]
不会产生使用显式创建方法显示的原始类型警告。缺少原始类型警告似乎不是违规行为(据我所知§4.8)并且如果List<String>[]
创建了必需的<{1}}则不会出现问题em>未选中警告。
答案 1 :(得分:10)
我能想到的最好的是JLS指定对泛型类型的构造函数的方法引用推断出泛型参数:
&#34;如果方法或构造函数是通用的,则可以推断或明确提供适当的类型参数。&#34;稍后它以ArrayList::new
为例,将其描述为&#34;推断通用类的类型参数,&#34;从而确定ArrayList<>::new
(而不是public static class Test<T> {
public Test() {}
}
)是推断参数的语法。
给出一个课程:
Test<String> = new Test(); // No <String>
这会发出警告:
Supplier<Test<String>> = Test::new; // No <String> but no warning
但这并不是:
Test::new
因为<html>
<head>
<title>Page name @</title>
</head>
</html>
隐含地推断了泛型参数。
所以我假设对数组构造函数的方法引用的工作方式相同。
答案 2 :(得分:5)
它仍然使用原始类型来创建数组,对吗?
Java泛型只是一种编译错觉,所以原始类型当然会在运行时用来创建数组。
为什么编译器在最后一种情况下不显示警告?
是的,从Test[]
到Test<String>[]
的未经检查的演员表仍在进行中;它只是在一个匿名的背景下发生在幕后。
Test<String>[] t3 = ((IntFunction<Test<String>[]>) Test[]::new).apply(10);
由于anonymous method正在执行脏工作,因此未经检查的强制转换会从托管代码中消失。