ern*_*t_k 13 java generics lambda javac ecj
我在回答一个问题时遇到了一个我无法解释的场景。考虑这个代码:
interface ConsumerOne<T> {
void accept(T a);
}
interface CustomIterable<T> extends Iterable<T> {
void forEach(ConsumerOne<? super T> c); //overload
}
class A {
private static CustomIterable<A> iterable;
private static List<A> aList;
public static void main(String[] args) {
iterable.forEach(a -> aList.add(a)); //ambiguous
iterable.forEach(aList::add); //ambiguous
iterable.forEach((A a) -> aList.add(a)); //OK
}
}
Run Code Online (Sandbox Code Playgroud)
我不明白为什么显式键入 lambda 的参数(A a) -> aList.add(a)
会使代码编译。此外,为什么它链接到 inIterable
而不是in 的重载CustomIterable
?
对此是否有一些解释或规范相关部分的链接?
注意:iterable.forEach((A a) -> aList.add(a));
仅在CustomIterable<T>
扩展时编译Iterable<T>
(完全重载方法CustomIterable
导致歧义错误)
在两者上都得到这个:
编辑:当 Eclipse 成功编译最后一行代码时,上面的代码在使用 maven 构建时无法编译。
TL; DR,这是一个编译器错误。
没有规则会在继承或默认方法时优先考虑特定的适用方法。有趣的是,当我将代码更改为
interface ConsumerOne<T> {
void accept(T a);
}
interface ConsumerTwo<T> {
void accept(T a);
}
interface CustomIterable<T> extends Iterable<T> {
void forEach(ConsumerOne<? super T> c); //overload
void forEach(ConsumerTwo<? super T> c); //another overload
}
Run Code Online (Sandbox Code Playgroud)
该iterable.forEach((A a) -> aList.add(a));
语句在 Eclipse 中产生错误。
由于在声明另一个重载时,接口中forEach(Consumer<? super T) c)
方法的属性没有Iterable<T>
改变,因此 Eclipse 选择此方法的决定不能(始终)基于该方法的任何属性。它仍然是唯一的继承方法,仍然是唯一的default
方法,仍然是唯一的JDK方法,等等。无论如何,这些属性都不应该影响方法选择。
请注意,将声明更改为
interface CustomIterable<T> {
void forEach(ConsumerOne<? super T> c);
default void forEach(ConsumerTwo<? super T> c) {}
}
Run Code Online (Sandbox Code Playgroud)
也会产生“模棱两可”的错误,因此适用的重载方法的数量也无关紧要,即使只有两个候选方法,也没有对default
方法的普遍偏好。
到目前为止,问题似乎出现在有两个适用的方法和一个default
方法和一个继承关系时,但这不是进一步挖掘的合适地方。
但是可以理解的是,您的示例的构造可能由编译器中的不同实现代码处理,一个显示错误而另一个没有。
a -> aList.add(a)
是一个隐式类型的lambda 表达式,不能用于重载决议。相比之下,(A a) -> aList.add(a)
是一个显式类型化的lambda 表达式,可用于从重载方法中选择匹配的方法,但它在这里没有帮助(在这里应该没有帮助),因为所有方法都具有具有完全相同功能签名的参数类型.
举个反例,用
static void forEach(Consumer<String> c) {}
static void forEach(Predicate<String> c) {}
{
forEach(s -> s.isEmpty());
forEach((String s) -> s.isEmpty());
}
Run Code Online (Sandbox Code Playgroud)
函数签名不同,使用显式类型的 lambda 表达式确实有助于选择正确的方法,而隐式类型的 lambda 表达式无济于事,因此forEach(s -> s.isEmpty())
会产生编译器错误。所有 Java 编译器都同意这一点。
请注意,这aList::add
是一个不明确的方法引用,因为该add
方法也已重载,因此它也无法帮助选择方法,但无论如何方法引用可能会被不同的代码处理。切换到无歧义aList::contains
或更改List
为Collection
,使之add
无歧义,并没有改变我的 Eclipse 安装(我使用2019-06
)的结果。
归档时间: |
|
查看次数: |
396 次 |
最近记录: |