Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compilation Error: Lambda Target Type Intersection Type

public class X {
  Object o = (I & J) () -> {};
}
interface I {
   public void foo(); 
}
interface J {
   public void foo();
   public void bar(); 
}

Oracle compiler throws a error:

 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 compiler compiles fine.

Which implementation looks correct ?

A modified form of the above example:

public class X {
  Object o = (I & J) () -> {};
}
interface I {
   public void foo(); 
}
interface J {
   public void foo();
}

Eclipse compiler throws a error. Oracle compiler accepts it.

I think Oracle compiler is correct.

Consider the test case:

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();
    }

}

The output with Oracle and Eclipse Compiler is:

foo J
bar I

Based on the output I can conclude that Oracle looks correct.

Let me know how you guys interpret it.

Thanks

like image 641
gudge Avatar asked Oct 01 '22 08:10

gudge


2 Answers

In the first example, I&J is not a functional interface (interface with exactly one abstract non-Object method). So javac is correct to give an error.

In the second case, I&J is a functional interface, so javac is again correct.

Sounds like two bugs in the Eclipse compiler.

like image 107
Brian Goetz Avatar answered Oct 04 '22 21:10

Brian Goetz


There is apparently confusion (by the authors of Eclipse compiler) about the rules governing intersection-typed lambdas. For

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

Eclipse complains that

The target type of this expression is not a functional interface: more than one of the intersecting interfaces are functional.

implying their understanding is that the intersection type should not be considered as a whole, but that exactly one of its component types must be a functional interface.

On the other hand, the rules which govern Oracle's compiler state that the resulting intersection type must itself (as a whole) be a functional interface.

Here is a related Eclipse bug report, where from the comments one can infer their misunderstanding. Key quote:

  • intersection casts for lambdas are now supported correctly. We no longer assume that the first entry in an intersection cast is the SAM type, we work out which one it is (if there is one!).

Note the word one. So they wrongly assume that these two things cannot happen:

  • the intersection type may contain types which have more than one method, and this must be guarded against with a compiler error;
  • the intersection type may contain several SAM types with identical methods, merging into a legal SAM type.

Their confusion (or lack of sufficient attention) apparently stems from their assumption that the only relevant context where intersection-typed lambdas arise is when a single SAM type is combined with marker interfaces, which have zero abstract methods.

BTW looking into Oracle compiler's output for this line of code:

I o = (I & J) () -> {};

this is what I found:

0: invokedynamic #2,  0              // InvokeDynamic #0:foo:()Ltest/Main$J;
5: checkcast     #3                  // class test/Main$I

Note that the InvokeDynamic call is placed with the type J, but the result is cast to I—and it succeeds. This seems like quite a subtlety.

like image 22
Marko Topolnik Avatar answered Oct 04 '22 21:10

Marko Topolnik