如果我对JSE for Java SE
8的第15和18章的理解是正确的,那么问题的关键在于第15.12.2段中的以下引文:
某些包含隐式类型的lambda表达式(§15.27.1)或不精确的方法引用(§15.13.1)的参数表达式将被适用性测试忽略,因为在选择目标类型之前,无法确定其含义。
当Java编译器遇到诸如之类的方法调用表达式时
test(() ->"test"),它必须搜索可将此方法调用分派到的可访问(可见)和适用(即具有匹配签名)的方法。在第一个示例中,
<T> voidtest(T)和
<T> void test(Supplier<T>)均可通过
test(() ->"test")方法调用访问和适用。在这种情况下,当存在多个匹配方法时,编译器将尝试确定最具体的方法。现在,尽管对通用方法的确定(如 JLS
15.12.2.5和JLS
18.5.4所述)相当复杂,但我们可以使用15.12.2.5开头的直觉:
非正式的直觉是,如果第一种方法处理的任何调用都可以传递给另一个方法而没有编译时错误,则一个方法比另一种方法更具体。
因为对于任何有效的调用
<T> void test(Supplier<T>),我们可以找到的类型参数的对应的实例
T中
<T> voidtest(T),前者比后者更具体。
现在,令人惊讶的部分是,在您的第二个示例中,两者<T> void test(Class<T>, Supplier<T>)
和<T> voidtest(Class<T>, T)
都被认为适用于方法调用test(String.class, () ->"test")
,即使我们很清楚,后者也不应该。问题是,如上所述,在存在隐式类型的lambda的情况下,编译器的行为非常保守。特别参见JLS
18.5.1:
一组约束公式C的构造如下。
…
- 要通过严格调用来测试适用性:
如果k≠n,或者存在i(1≤i≤n) 使得e_i与适用性有关 (§15.12.2.2)(…) 否则,对于所有i(1≤i≤k
),其中e_i与适用性有关,‹e_i→F_iθ›。
- 要通过松散调用来测试适用性:
如果k≠n,则该方法不适用,并且无需进行推理。
否则,对于e_i与适用性有关的所有i(1≤i≤k),C包括‹e_i→F_iθ›。
和JLS
15.12.2.2:
除非参数表达式具有以下形式之一,否则认为它 与 可能适用的方法m的 适用性有关 :
- 隐式类型的lambda表达式(第15.27.1节)。
…
因此,在方法适用性检查的上下文中,作为参数传递的隐式类型的lambda的约束不参与解决类型推断。
现在,如果我们假设这两种方法均适用,那么问题以及本例与前面的示例之间的区别是,这些方法都没有一个更具体。存在对无效
<T> voidtest(Class<T>, Supplier<T>)但对无效的调用,
<T> void test(Class<T>, T)反之亦然。
这也解释了为什么
test(String.class, (Supplier<String>) () ->"test");编译,就像@Aominè在上面的评论中提到的那样。
(Supplier<String>) () ->"test")是一个显式类型的lambda,因此被认为 与适用性有关 ,编译器能够正确推断出这些方法中只有一种适用,并且不会发生冲突。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)