Are there dangers in making an existing Java interface functional?

Viewed 353

As a rule, in the context of a large project, is it considered safe to take make an existing, ubiquitously used interface into a functional interface?

E.g., given an existing interface and class:

public interface Interface {
    public double calculateSomething(double x);
    public void doSomething();
}

which is implemented by

class InterfaceImplementer implements Interface {

     public double calculateSomething(double x) {
          return 2 * x;
     }

     public void doSomething() {
         // insert specific behavior here
     } 
}

can I safely change the interface by defining all but one method as default:

public interface Interface {
    public double calculateSomething(double x);

    default void doSomething() {
         // insert some default behavior here
    }
}

So that I can go from defining an object as

Interface object = new InterfaceImplementer() {

    @Override
    public double calculateSomething(double x) {
        return 2 * x;
    }
};

to

Interface object = (x) -> 2 * x;

while still being able to define objects in the old, tedious way.

From what I can tell, this runs no risk of upsetting any existing code, and I've made such a change to a large project and had no runtime or compile errors. But I want some confirmation whether this matches up with common knowledge and best practices.

4 Answers

Any interface that only has a single non-default method (only one method needs to be implemented in a class) is by definition a functional interface. This is a good rule!

However, a @FunctionalInterface annotation has the advantage of enforcing the "only one method in the interface for a functional interface"-rule. So if you added it to your original two-method interface, you would have gotten a compiler error. Therefore by explicitly adding @FunctionalInterface you declare your intent and make your code more clear to future maintainers.

On java code level, I can think of one problem: since this interface already had contained 2 methods at some point in the past, you may want to add another method to it later on. You won't be able to add another method to a functional interface, since it has to remain a functional interface so you can use it as a functional interface. You will have to create an interface that inherits from this one. Which leads me to the main point.

It may have been logical to have those 2 methods in one interface before, but is it really logical now? Refactor the code, separate the interfaces; either make one extend another or use an interface that inherits from both, your call. If the interface is to be used as a functional one, make it functional. It will be clean. It will be understandable. You will be able to add methods to one of those interfaces in the future without further refactoring.

The Java API states:

However, the compiler will treat any interface meeting the definition of a functional interface as a functional interface regardless of whether or not a FunctionalInterface annotation is present on the interface declaration.

Therefore there is no risk to add that annotation.

Well, it's not the @Functional that might break anything, but adding a default implementation may lead to compilation errors for abstract classes that implement or interfaces that extend multiple interfaces declaring methods with override-equivalent signatures:

The following compiles fine:

interface I {
    void a();
}

interface J {
    void a();
}

interface K extends I, J {}

while this doesn't:

interface I {
    default void a() {}
}

interface J {
    void a();
}

interface K extends I, J {}

The default method a() inherited from I conflicts with another method inherited from J

So if you do this in a library, code using this may fail to compile after the change.

Related