Functional interface confusion

Stephen Colebourne scolebourne at joda.org
Sun Jul 7 02:25:15 PDT 2013


Its certainly a pain for me in this case. I understand why its
half-allowed (references only), but I think it has certain issues
still.

Firstly, IntelliJ Idea IDE editor indicated that @FunctionalInterface
was invalid for the interface here. But the compiler did not. So there
is an inconsistency. Given the below, it sounds like a bug in
IntelliJ. If not, then the @FunctionalInterface Javadoc needs
updating.

Secondly, I would prefer that this was valid as a way to resolve the problem:
static <T extends Temporal> TemporalAdjuster firstDayOfMonth() {
  return (T temporal) -> (T) temporal.with(DAY_OF_MONTH, 1);
}

Not that it is overly useful as a general construct (it doesn't solve
my problem here), just that it would be easier to explain in a
learning environment, and no doubt occasionally useful. I know you
don't want to hear it, but not supporting the solution above feels
like a bug in the spec to me, given that the whole purpose of defining
actual types in the lambda parameter area is to express a
non-inferable type.

(To be clear, I'm happy enough to agree with the absence of special
syntax on the lambda itself for declaring T)

@Remi, using a method reference is appropriate for the example, but
not in general, as end users will be using this. As such, I have no
choice but to leave it without generics, which makes me sad.

Stephen



On 7 July 2013 02:44, Brian Goetz <brian.goetz at oracle.com> wrote:
> 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