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