编译错误:Lambda目标类型交集类型

public class X { Object o = (I & J) () -> {}; } interface I { public void foo(); } interface J { public void foo(); public void bar(); } 

Oracle编译器抛出错误:

  X.java:2: error: incompatible types: INT#1 is not a functional interface Object o = (I & J) () -> {}; ^ multiple non-overriding abstract methods found in interface INT#1 where INT#1 is an intersection type: INT#1 extends Object,I,J 1 error 

Eclipse编译器编译得很好。

哪种实现看起来正确?

以上示例的修改forms:

 public class X { Object o = (I & J) () -> {}; } interface I { public void foo(); } interface J { public void foo(); } 

Eclipse编译器抛出错误。 Oracle编译器接受它。

我认为Oracle编译器是正确的。

考虑测试用例:

 interface I { default void foo() { System.out.println("foo I \n"); } default void bar() { System.out.println("bar I \n"); } } interface J extends I { default void foo() { System.out.println("foo J \n"); } } public class Y { public static void main(String argv[]) throws Exception { J j = new J() { }; ((I & J) j).foo(); ((I & J) j).bar(); } 

}

Oracle和Eclipse Compiler的输出是:

 foo J bar I 

根据输出,我可以得出结论,Oracle看起来是正确的。

让我知道你们是如何解释它的。

谢谢

在第一个例子中, I&J不是一个function接口(只有一个抽象的非Object方法的接口)。 所以javac给出错误是正确的。

在第二种情况下, I&J是一个function接口,所以javac再次正确。

听起来像Eclipse编译器中的两个错误。

显然混淆了(由Eclipse编译器的作者)关于交叉型lambda的规则。 对于

 interface I { void foo(); } interface J { void foo(); } 

Eclipse抱怨说

此表达式的目标类型不是function接口:多个交叉接口是有效的。

暗示他们的理解是交叉类型不应该被视为一个整体,但它的一个组件类型必须是一个function接口。

另一方面,管理Oracle编译器状态的规则表明生成的交集类型本身(作为一个整体)必须是一个function接口。

这是一个相关的Eclipse错误报告,从评论中可以推断出他们的误解。 关键报价:

  • 现在可以正确支持lambdas的交集转换。 我们不再假设交叉投射中的第一个条目是SAM类型,我们计算出它是哪一个(如果有的话!)。

注意单词one 。 所以他们错误地认为这两件事不可能发生:

  • 交集类型可能包含具有多个方法的类型,并且必须防止编译器错误;
  • 交集类型可以包含几种具有相同方法的SAM类型,合并为合法的SAM类型。

他们的困惑(或缺乏足够的关注)显然源于他们的假设,即交叉型lambda出现的唯一相关背景是当单个SAM类型与标记接口组合时,其具有零抽象方法。

BTW在这行代码中查看Oracle编译器的输出:

 I o = (I & J) () -> {}; 

这是我发现的:

 0: invokedynamic #2, 0 // InvokeDynamic #0:foo:()Ltest/Main$J; 5: checkcast #3 // class test/Main$I 

请注意, InvokeDynamic调用的类型为J ,但结果将转换为I – 并且成功。 这看起来非常微妙。

第一个例子:

您正在将lambda表达式转换为交集类型。 如果强制转换的结果是有效的lambda表达式,那么它必须是Java语言规范(JLS)中定义的有效function接口 。 在我看来,Oracle编译器是正确的,因为这是一个无效的强制转换。 我不知道它在JLS中打破的确切规则,但我想它在参考类型转换部分(JLS 5.5.1)中列出。

第二个例子:

由于方法foo()在两个接口中都被声明,因此它将作为实现两者的任何具体类中的一个方法合并。 因此任何交集类型只有一个抽象方法; 这使它成为一个有效的function界面。 Oracle编译器展示了正确的行为。

边注:

如果您希望接口为lambda表达式定义方法签名,那么请确保使用@FunctionalInterface对其进行批注,以便增量Eclipse编译器检查以确保您的接口被视为有效的function接口。 这会从第一个示例中为接口J引发错误,因为它有多个抽象方法。