Java 编译错误:Lambda目标类型交叉点类型
Oracle编译器引发错误:Java 编译错误:Lambda目标类型交叉点类型,java,eclipse,lambda,java-8,Java,Eclipse,Lambda,Java 8,Oracle编译器引发错误: public class X { Object o = (I & J) () -> {}; } interface I { public void foo(); } interface J { public void foo(); public void bar(); } Eclipse编译器编译得很好 哪个实现看起来正确 上述示例的修改形式: X.java:2: error: incompatible types: IN
public class X {
Object o = (I & J) () -> {};
}
interface I {
public void foo();
}
interface J {
public void foo();
public void bar();
}
Eclipse编译器编译得很好
哪个实现看起来正确
上述示例的修改形式:
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编译器抛出一个错误。Oracle编译器接受它
我认为Oracle编译器是正确的
考虑一下测试用例:
public class X {
Object o = (I & J) () -> {};
}
interface I {
public void foo();
}
interface J {
public void foo();
}
}
Oracle和Eclipse编译器的输出为:
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看起来是正确的
让我知道你们是怎么解释的
感谢在第一个示例中,
I&J
不是一个功能接口(仅具有一个抽象非对象方法的接口)。所以javac给出一个错误是正确的
在第二种情况下,I&J
是一个功能接口,因此javac也是正确的
听起来像Eclipse编译器中的两个bug。第一个示例:
您正在将lambda表达式强制转换为交集类型。如果强制转换的结果是有效的lambda表达式,那么它必须是有效的。在我看来,Oracle编译器是正确的,因为这是一个无效的强制转换。我不知道确切的规则,它打破了在JLS,但我想它是在列表中列出的
第二个例子:
由于方法foo()
是在两个接口中声明的,因此它将作为一个方法合并到实现这两个接口的任何具体类中。因此,任何交叉点类型都只有一个抽象方法;这使它成为一个有效的功能接口。Oracle编译器显示了正确的行为
旁注:
如果希望接口为lambda表达式定义方法签名,请确保使用
@functionanterface
对其进行注释,以便增量Eclipse编译器将进行检查,以确保您的接口被视为有效的函数接口。这将从您的第一个示例中为接口J抛出一个错误,因为它有多个抽象方法。显然(Eclipse编译器的作者)对控制交集类型lambda的规则感到困惑。为了
foo J
bar I
Eclipse抱怨说
此表达式的目标类型不是函数接口:多个相交接口是函数接口
暗示他们的理解是,不应将交叉点类型视为一个整体,但其组件类型中必须有一个是功能接口
另一方面,控制Oracle编译器的规则规定,生成的交集类型本身(作为一个整体)必须是函数接口
是一个相关的Eclipse bug报告,从中可以推断出他们的误解。关键报价:
- 现在正确支持lambda的交叉点强制转换。我们不再假设交叉点转换中的第一个条目是SAM类型,而是确定它是哪一个(如果有!)
- 交集类型可能包含具有多个方法的类型,必须防止出现编译器错误李>
- 交叉点类型可能包含多个具有相同方法的SAM类型,并合并为合法SAM类型
interface I { void foo(); }
interface J { void foo(); }
这就是我发现的:
I o = (I & J) () -> {};
请注意,
InvokeDynamic
调用的类型为J
,但结果被转换为I
,并且成功。这看起来很微妙。@Keppil Eclipse编译器。什么是Eclipse编译器?顺便问一下,在第一种情况下,您期望得到什么,在交叉点中有两个方法=>接口不起作用?我正在试图弄清楚交叉点在这种情况下的含义。我试图找到一个独立于lambdas的例子。在我看来,甲骨文在这种情况下是正确的。我得核实一下。
0: invokedynamic #2, 0 // InvokeDynamic #0:foo:()Ltest/Main$J;
5: checkcast #3 // class test/Main$I