shmosel
shmosel

Reputation: 50716

Functional Interface Inheritance Quirk

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?

Upvotes: 20

Views: 7158

Answers (1)

gontard
gontard

Reputation: 29520

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();

Upvotes: 10

Related Questions