I recently came across this when trying to provide common functionality to a sub-class of a functional interface. Apparently you cannot do it the way I thought (atleast for Java 8):
@FunctionalInterface
public interface Base<T extends Base<T>> {
void doit();
default T someDefault() {
return () -> {}; //incompatible types: T is not a functional interface
}
}
Is there a way to do this? Or was it intentionally disallowed? Maybe an abuse of default methods?
Maybe a use-case example would be a good idea:
@FunctionalInterface
public interface Processor<I, T extends Processor<I, T>> {
T process(I input);
default T requireNonNull(I i) {
return (I input) -> {
Objects.requireNonNull(i);
process(input);
};
}
}
@FunctionalInterface
public interface Parser<T> extends Processor<String, T> {
@Override
default T process(String s) {
return parse(s);
}
T parse(String s);
}
As said in a comment, it is not guaranteed the interface extending the Base interface can be expressed the same way. The only possible is to change the return type to Base<T> to make returning the lambda legal, which is guaranteed by the Base interface:
default Base<T> someDefault() {
return () -> {};
}
Though it is probably what you don't want. Remember that the ultimate condition you need to satisfy that an interface can be expressed through a lambda expression is that the interface has exactly one abstract (non-default) method, which can be achieved also through extending a different interface.
Here is an example using the Base interface that demonstrates why it is not possible to achieve the way you do:
public interface StringBase extends Base<StringBase> {
void bonk();
}
Now StringBase cannot be expressed through a lambda expression as long as it provides two abstract methods.
Theoretically, only and only if all interfaces extending the Base interface can be compiled with @FunctionalInterface would assure that each of them would satisfy such a condition. However, there is no mechanism in Java to enforce this for the sake of successful compilation.
One of the workarounds is to implement the someDefault method and provide it as default in the extending interface. This assures the instantiated interface has truly a single abstract method and there is no dispute as in the Base interface:
public interface Base<T extends Base<T>> {
void doIt();
T someDefault();
}
@FunctionalInterface
public interface StringBase extends Base<StringBase> {
@Override
default StringBase someDefault() {
return () -> {};
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With