【问题标题】:Compilation Error: Lambda Target Type Intersection Type编译错误:Lambda 目标类型交集类型
【发布时间】:2014-07-11 08:32:19
【问题描述】:
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 编译器的输出是:

foo J
bar I

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

让我知道你们是怎么理解的。

谢谢

【问题讨论】:

  • @Keppil Eclipse 编译器。
  • 什么是eclipse编译器?
  • 顺便说一句,你对第一种情况有什么期望,在交叉点有两个方法 => 接口不起作用?
  • 我试图弄清楚在这种情况下交集案例是什么意思。我正在尝试找到一个独立于 lambdas 的示例。
  • 我读过docs.oracle.com/javase/specs/jls/se5.0/html/…。在我看来,Oracle 在这种情况下是正确的。我将不得不验证。

标签: java eclipse lambda java-8


【解决方案1】:

在第一个示例中,I&J 不是函数式接口(仅与一个抽象非对象方法的接口)。所以javac给出错误是正确的。

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

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

【讨论】:

    【解决方案2】:

    对于管理交集类型 lambda 的规则,显然存在混淆(Eclipse 编译器的作者)。对于

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

    Eclipse 抱怨

    此表达式的目标类型不是函数式接口:多个相交接口是函数式的。

    暗示他们的理解是交集类型不应该被视为一个整体,而其组件类型中的一个必须是功能接口。

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

    Here 是一份相关的 Eclipse 错误报告,从 cmets 可以推断出他们的误解。关键引用:

    • 现在可以正确支持 lambda 的交集强制转换。我们不再假设交集转换中的第一个条目是 SAM 类型,而是确定它是哪一个(如果有的话!)。

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

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

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

    顺便说一句,查看 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——它成功了。这似乎很微妙。

    【讨论】:

      【解决方案3】:

      第一个例子:

      您正在将 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 抛出错误,因为它有多个抽象方法。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-09-17
        • 1970-01-01
        • 2013-12-12
        • 2016-04-05
        • 2017-09-22
        • 2016-05-06
        • 2014-08-12
        相关资源
        最近更新 更多