Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 801
shmosel Avatar asked Jun 04 '15 03:06

shmosel


People also ask

Can functional interface be inherited?

You can't inherit any functional interface to another functional interface. Because it breaks the law of functional interface has exactly one abstract method.

Can a functional interface extend inherit another interface?

A functional interface can extends another interface only when it does not have any abstract method.

How many functional interfaces does Java 8 have?

In Java 8, there are 4 main functional interfaces are introduced which could be used in different scenarios.

What is the use of functional interface?

Functional interfaces are interfaces that ensure that they include precisely only one abstract method. Functional interfaces are used and executed by representing the interface with an annotation called @FunctionalInterface. As described earlier, functional interfaces can contain only one abstract method.


1 Answers

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();
like image 72
gontard Avatar answered Oct 22 '22 11:10

gontard