编译错误: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引发错误,因为它有多个抽象方法。