20

I have a custom interface I've been using for some time that looks something like this:

public interface Function<T, R> {
    R call(T input);
}

I'd like to retrofit this interface with both Java's Function as well as Guava's Function, while keeping it a FunctionalInterface. I thought I had the perfect arrangement:

@FunctionalInterface
public interface Function<T, R> extends
        java.util.function.Function<T, R>,
        com.google.common.base.Function<T, R> {

    R call(T input);

    @Override
    default R apply(T input) {
        return call(input);
    }
}

Both superinterfaces declare the same apply() method, which has been implemented in my interface, leaving only the abstract call() method. Strangely, it won't compile, telling me

Invalid '@FunctionalInterface' annotation; Function<T,R> is not a functional interface

Stranger still, the following variations compile just fine:

@FunctionalInterface
public interface Function<T, R> extends
        java.util.function.Function<T, R> {

    R call(T input);

    @Override
    default R apply(T input) {
        return call(input);
    }
}

@FunctionalInterface
public interface Function<T, R> extends
        com.google.common.base.Function<T, R> {

    R call(T input);

    @Override
    default R apply(T input) {
        return call(input);
    }
}

public interface Function<T, R> extends
        java.util.function.Function<T, R>,
        com.google.common.base.Function<T, R> {

    R call(T input);

    @Override
    default R apply(T input) {
        return call(input);
    }
}

@FunctionalInterface
public interface Function<T, R> extends
        java.util.function.Function<T, R>,
        com.google.common.base.Function<T, R> {

    @Override
    R apply(T input);
}

Is there a reason the first version won't compile?

8
  • 1
    Your third one compiles because you haven't added the constraint that it must be a @FunctionalInterface (that's an annotation validated by the compiler). Commented Jun 4, 2015 at 3:42
  • 1
    @SotiriosDelimanolis, obviously. Commented Jun 4, 2015 at 3:42
  • 6
    Are you compiling with Eclipse? Compiles fine with Oracle's compiler. Commented Jun 4, 2015 at 3:50
  • 1
    This should compile and does compile with javac. Looks like an eclipse bug.... Also I'm not sure why you want to do that - the main point of functional interfaces is to create lambdas and the name of the method does not matter. So this lambda a -> doSomething(a) can be assigned to any of the 3 Functions interfaces... Commented Jun 4, 2015 at 5:46
  • 1
    @assylias, say I have a method that accepts my Function and I want to refactor it to accept any Java Function. Or say I have an instance of my Function that I want to be able to pass to a Guava method. Commented Jun 4, 2015 at 6:32

1 Answer 1

10

As stated in the comments, it compiles fine with the oracle compiler. It is an eclipse bug.

Awaiting for a bug fix, personally i will remove the annotation @FunctionalInterface (your 3rd variation):

public interface Function<T, R>
                                extends
                                    java.util.function.Function<T, R>,
                                    com.google.common.base.Function<T, R> {

    R call(T input);

    @Override
    default R apply(T input) {
        return call(input);
    }
}

The major inconvenient of this solution is that the eclipse compiler bug prevent from using the Function as a lambda target type.


If you really want to keep @FunctionalInterface on your Function, a (ugly) workaround might be to introduce an intermediate interface:

public interface AdapterFunction<T, R>
                                      extends
                                          java.util.function.Function<T, R>,
                                          com.google.common.base.Function<T, R> {
    @Override
    default R apply(T input) {
        return null;
    }
}

and let your Function extends this AdapterFunction:

@FunctionalInterface
public interface Function<T, R>
                                extends
                                    AdapterFunction<T, R> {

    R call(T input);

    @Override
    default R apply(T input) {
        return call(input);
    }
}

In this case, the Function is a valid target type for eclipse too:

Function<String, Object> function = st -> st.toString();
Sign up to request clarification or add additional context in comments.

8 Comments

Yes, I found a similar bug report here. I don't care about the annotation itself; the problem is that it won't let me use lambda expressions. I tried something similar your adapter idea, with no success. I'll try again tomorrow.
The solution with the AdapterFunction works in eclipse too. But removing the @FunctionalInterface (3rd variation) don't
"The major inconvenient of this solution is that you couldn't use the Function as a lambda target type" => Why not? You don't need an interface to be marked as @FunctionalInterface ot be used as a target type for a lambda, as long as it effectively is a functional interface...
@assylias you are theoretically right but in practice the eclipse compiler does not accept it. It works only with the oracle compiler
@assylias: obviously, Eclipse uses the same code to determine whether @FunctionalInterface can be applied and whether a lambda expression’s target type is a functional interface. Which makes sense. So you don’t need to have an @FunctionalInterface annotation, but you need a compiler with a correct test for functional interface compliance. I suspect, this version of Eclipse would reject that interface even with the annotation, as the presence of the annotation is not sufficient for the compiler.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.