Welcome! Please see the About page for a little more info on how this works.

+1 vote
in Compiler by

While implementing Cursive's support for the new functional interface conversion, I found some inconsistencies between how Java sees these and how Clojure does. I wanted to check whether these were deliberate or not.

  1. Clojure requires the class containing the SAM (single abstract method) to have the FunctionalInterface annotation to be eligible for the conversion. However Java does not have this restriction, and the JLS does not mention it: https://docs.oracle.com/javase/specs/jls/se21/html/jls-9.html#jls-9.8. This might be problematic because it's entirely possible that there are classes in libraries out there without the annotation which can be used with lambdas in Java, but not in Clojure. This could easily happen for classes in older pre-lambda libraries. It seems that FunctionalInterface is more to ensure that an interface intended to be a SAM remains so and doesn't accidentally have methods added to it (https://docs.oracle.com/javase/specs/jls/se21/html/jls-9.html#jls-9.6.4.9). In fact, that section explicitly states that the annotation is not required for an interface to be functional.
  2. Similarly, the spec says that the functional interface must be an interface, but Compiler.FISupport.maybeFIMethod() does not check this, it only checks that the method itself is abstract. So Clojure would accept an abstract class containing a SAM, but Java does not. This one seems less likely to occur in practice, but is also easy to tighten up, and I can't see a useful use case for not enforcing this.

2 Answers

0 votes
by
by
Thanks, I hadn't seen that. Unfortunate that this will not be fixed in 1.12, this seems like it is likely to bite people.

Regardless, I guess I'll implement what the compiler currently does.
0 votes
by
edited by

1) Our goal here was to find places where Clojure functions have a semantic match to Java constructs and make their use in that place implicit. Functional interfaces marked as such must have a single method and have clearly stated that intent. SAM interfaces, while structurally similar, do not have that stated intent and may not be a semantic match to Clojure functions (instead being "procedures" or some non-functional idea).

That said, the combination of SAMs and method values is an interesting convergence point that we are still working on. If you are passing a method value to a method taking a SAM, that is a point where we can directly adapt (as Java does with method references) without creating a method value wrapper and adapting that. That is a use where there is no Clojure function and no need for a semantic match and we may support that, still TBD.

2) maybeFIMethod checks that the class has the @FunctionalInterface annotation, which can only exist on interfaces, and thus it is redundant to check if it is an interface.

...