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编译器编译良好。
哪种实现看起来正确?
以上示例的修改形式:
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看起来是正确的。
让我知道你们是如何解释的。
由于
答案 0 :(得分:2)
在第一个示例中,I&J
不是功能接口(仅具有一个抽象非Object方法的接口)。所以javac给出错误是正确的。
在第二种情况下,I&J
是一个功能界面,所以javac再次正确。
听起来像Eclipse编译器中的两个错误。
答案 1 :(得分:2)
Eclipse编译器的作者显然对管理交集类型lambda的规则感到困惑。对于
interface I { void foo(); }
interface J { void foo(); }
Eclipse抱怨
此表达式的目标类型不是功能接口:多个交叉接口可以正常工作。
暗示他们的理解是不应将交集类型视为一个整体,但其组件类型中的一个必须是一个功能接口。
另一方面,管理Oracle编译器状态的规则表明生成的交集类型本身(作为一个整体)必须是一个功能接口。
Here是一个相关的Eclipse错误报告,从评论中可以推断出他们的误解。关键报价:
- 现在可以正确支持lambdas的交集转换。我们不再假设交叉投射中的第一个条目是SAM类型,我们计算出它是哪一个(如果有的话!)。
注意单词 one 。所以他们错误地认为这两件事情都不会发生:
他们的混淆(或缺乏足够的注意力)显然源于他们的假设,即交叉型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
- 并且成功。这看起来非常微妙。
答案 2 :(得分:0)
您正在将lambda表达式转换为交集类型。如果强制转换的结果是有效的lambda表达式,那么它必须是有效的functional interface as defined in the Java Language Specification (JLS)。在我看来,Oracle编译器是正确的,因为这是一个无效的强制转换。我不知道它在JLS中打破的确切规则,但我想它已在reference type casting section (JLS 5.5.1)中列出。
由于方法foo()
在两个接口中都被声明,因此它将作为实现两者的任何具体类中的一个方法合并。因此任何交集类型只有一个抽象方法;这使它成为一个有效的功能界面。 Oracle编译器表现出正确的行为。
如果您希望接口为lambda表达式定义方法签名,请确保使用@FunctionalInterface
对其进行批注,以便增量Eclipse编译器检查以确保您的接口被视为有效的功能接口。这将从第一个示例中为接口J引发错误,因为它有多个抽象方法。