Java 编译错误:Lambda目标类型交叉点类型

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

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: 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类型,而是确定它是哪一个(如果有!)
注意单词one。因此他们错误地认为这两件事不可能发生:

  • 交集类型可能包含具有多个方法的类型,必须防止出现编译器错误
  • 交叉点类型可能包含多个具有相同方法的SAM类型,并合并为合法SAM类型
他们的困惑(或缺乏足够的关注)显然源于他们的假设,即交叉类型lambda出现的唯一相关上下文是单个SAM类型与标记接口组合时,标记接口没有抽象方法

顺便说一句,查看Oracle编译器输出的这行代码:

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