Functional interface confusion

Brian Goetz brian.goetz at oracle.com
Sat Jul 6 18:44:17 PDT 2013


You've pretty much figured it out.

There was some question as to whether to allow the single method in a 
SAM to be a generic method or not.  There's no theoretical problem with 
SAMs that have generic methods, and this works perfectly well with 
method refs: you can assign a method ref for a generic method to a 
(compatible) SAM with a generic method.  But for lambdas, there's no 
syntax for it; you'd need extra syntax to name the type parameter.  (No, 
please don't suggest any; they've all been considered.)  Note that there 
are no problems with SAMs being generic classes; we're only talking 
about methods that themselves introduce generic type parameters, like 
your TemporalAdjuster example.

In the end we decided to go the slightly odd middle ground of not 
prohibiting SAMs with generic methods (after all, they can be used 
perfectly well with method refs) but not inventing a syntax for generic 
lambdas.  We're pretty comfortable with this choice; it comes up rarely, 
and in those rare cases, all you have to do is manually desugar the 
lambda to method, and take a method ref of that.  So TemporalAdjuster is 
a valid SAM, but its harder to use than most SAMs.  For API code, you 
probably do want to avoid this kind of functional interface.

On 7/6/2013 5:39 PM, Stephen Colebourne wrote:
> I tried to create this interface today, and the compiler complained:
>
> @FunctionalInterface
> public interface TemporalAdjuster {
>      <T extends Temporal> T adjustInto(T temporal);
> }
>
> static TemporalAdjuster firstDayOfMonth() {
>    return (temporal) -> temporal.with(DAY_OF_MONTH, 1);
> }
>
> java: incompatible types: invalid functional descriptor for lambda expression
>      method <T>(T)T in interface java.time.temporal.TemporalAdjuster is generic
>
> The same error was seen with this:
> static <T extends Temporal> TemporalAdjuster firstDayOfMonth() {
>    return (T temporal) -> (T) temporal.with(DAY_OF_MONTH, 1);
> }
>
> I must admit that I wasn't expecting this, as nothing in the
> documentation of @FunctionalInterface or other recent discussions that
> I have seen indicated to me that there is a limitation on lambdas and
> method-based generics. (FYI, method-based generics are correct here.
> Generifying the interface TemporalAdjuster would not represent the
> same logical thing in the codebase. The only constraint is on the
> method.)
>
> By contrast, this seems to work fine:
>
> @FunctionalInterface
> public interface TemporalQuery<R> {
>      R queryFrom(TemporalAccessor temporal);
> }
> static final TemporalQuery<ZoneId> ZONE_ID = (temporal) -> {
>    return temporal.query(ZONE_ID);
> };
>
>
> Is the compiler wrong? Is the spec of FunctionalInterface incomplete
> (perhaps deliberately)?
> (compiler I'm using is old and on the
> http://hg.openjdk.java.net/threeten/threeten/jdk branch)
>
> I think I understand why the first example cannot be a lambda, as
> there is no way to grab the <T> in the lambda body, but I'd like a
> proper explanation! It definitely feels like an awkward edge case, and
> I suspect I'll be forced back to the ungenerified version.
>
> thanks
> Stephen
>


More information about the lambda-dev mailing list