Functional interface confusion

Brian Goetz brian.goetz at oracle.com
Sun Jul 7 07:28:37 PDT 2013


It is valid to be uncomfortable with the middle ground solution we 
chose, but you should consider what the alternatives were.

But, keep in mind the alternatives.  The only realistic alternative was 
"functional interfaces cannot have generic methods."  This is a simpler 
and more stable solution, but also a more unfortunate one, and which 
would lead to even more bitching about "assigning a method ref for a 
generic method to a generic SAM would work perfectly and would require 
no new syntax, so why can't I?"

Returning to your proposal, it doesn't make sense to me.  The 
firstDayOfMonth() method does *not* return something generic; it returns 
a lambda specialized to a specific T.  That's not compatible with the 
function contract for a generic TemporalAdjuster.

Remember, when you *invoke* a generic method, the type variables are 
chosen at the call site.  If you did:

   TemporalAdjuster ta = TA.<T>firstDayOfMonth();

for the purpose of the body, you've selected a T.  But a users is free 
to do:

   ta.<U>adjustInto(u);

So what you're suggesting here doesn't make sense when interpreted 
according to Java generics.  Really, this is just a proposal for a 
generic lambda syntax in disguise, by overloading another syntax (and 
one that means something else.)

Its OK to be sad about this, but there really isn't a credible 
alternative short of inventing a syntax for generic lambdas, which we 
didn't want to do.

On 7/7/2013 5:25 AM, Stephen Colebourne wrote:
> 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